1- 指针和一维数组
一维数组名: 一维数组名是个指针常量, 他存放的是一维数组第一个元素的地址.
- #include <stdio.h>
- /*
- 总结:
- 一维数组名
- 一维数组名是个指针常量
- 它存放的是一维数组第一个元素的地址
- */
- int main(void)
- {
- int a[5]; //a 是数组名 5 是数组元素的个数 元素就是变量 a[0] -- a[4]
- // int a[3][4]; //3 行 4 列 a[0][0]是第一个元素 a[i][j]第 i+1 行 j+1 列
- int b[5];
- //a = b;//error a 是常量
- printf("%#X\n", &a[0]);
- printf("%#X\n", a);
- return 0;
- }
常量是不能被改变的, 也就是说, 一维数组名是不能被改变的.
数组名 a 存放的是一维数组第一个元素的地址, 也就是 a = &a.
2 - 下标和指针的关系
** 如果 p 是个指针变量, 则 p[i]永远等价于 *(p+i)**
- # include <stdio.h>
- int main(void)
- {
- int a[5] = {1,2,3,4,5};
- int i;
- for (i=0; i<5; ++i)
- printf("%d\n", a[i]); //a[i] == *(a+i)
- return 0;
- }
3- 确定一个一维数组需要几个参数
- # include <stdio.h>
- /*
- a 是个指针变量, 所以上面局部函数 f 的 pArr 则要定义成指针函数才可以, 而 len 则是 int 类型. 代表接收的是整型的数字.
- */
- //f 函数可以输出任何一个一维数组的内容
- void f(int * pArr, int len)
- {
- int i;
- for (i=0; i<len; ++i)
- printf("%d", *(pArr+i) ); //*pArr *(pArr+1) *(pArr+2)
- printf("\n");
- }
- int main(void)
- {
- int a[5] = {1,2,3,4,5};
- int b[6] = {-1,-2,-3,4,5,-6};
- int c[100] = {1, 99, 22, 33};
- f(a, 5); //a 是 int *
- f(b, 6);
- f(c, 100);
- return 0;
- }
- /*
- 1, 一定要明白 10 行的 pArr[3] 和 17 行 19 行的 a[3] 是同一个变量因为数组 a 的名称代表的是 a 的第一个元素的地址, 所以在函数 f 中所定义的指针变量 pArr 和 a 是相同的, 因为 a 也是指针类型.
- 2, 也就是说 pArr=a=a[0],pArr[1]=a[1]=*(pArr+1)=*(a+1),pArr[2]=a[2]=*(pArr+2) =*(a+2). 所以在 f 函数中 pArr[3]=a[3], 所以第二个 printf 输出的结果是 88. 总结: pArr[i] = a[i] = *(pArr+i) = *(a+i)
- 3, 在没有学习指针时, 可将 a[3]认为是数组中第 4 个元素, 这里下标也当成指针了, 从首元素开始向后移动 3 个, 即指向第 4 个元素.
- */
- # include <stdio.h>
- void f(int * pArr, int len)
- {
- pArr[3] = 88; //10 行
- }
- int main(void)
- {
- int a[6] = {1,2,3,4,5,6};
- printf("%d\n", a[3]); //17 行
- f(a, 6);
- printf("%d\n", a[3]); // 19 行
- return 0;
- }
- /*
- 因为数组 a 的名称代表的是 a 的第一个元素的地址, 所以在函数 f 中所定义的指针变量 pArr 和 a 是相同的, 因为 a 也是指针变量类型. 也就是说 pArr=a=a[0],pArr[1]=a[1]=*(pArr+1)=*(a+1),pArr[2]=a[2]=*(pArr+2) =*(a+2). 我们在 f 函数中修改数组的值, 相当于修改主函数中相对应的值.
- */
- # include <stdio.h>
- void f(int * pArr, int len)
- {
- int i;
- for (i=0; i<len; ++i)
- printf("%d", pArr[i]); //*(pArr+i) 等价于 pArr[i] 也等价于 b[i] 也等价于 *(b+i)
- printf("\n");
- }
- int main(void)
- {
- int b[6] = {-1,-2,-3,4,5,-6};
- f(b, 6);
- b[i]
- return 0;
- }
4- 何谓变量地址 / 一个指针变量占几个字节
Sizeof(变量名 / 数据类型) 其返回值就是该变量或数据类型所占字节数
== 一个指针变量无论其指向变量占几个字节, 其本身所占大小都是 4 字节.==
*p 具体指向几个字节, 要靠前面类型确定, 如果为 int 则为 4 字节, 如果 double 则占 8 字节.
CPU 与 内存 交互时 有 32 根线, 每根线只能是 1 或 0 两个状态, 所有总共有 232 个状态. 1 个状态 对应 一个单元. 如全为 0 全为 1 等. 内存中第一个单元, 即 32 根线状态全为 0.0000 0000 0000 0000 0000 0000 0000 0000 其大小为 4 字节
所有每个地址 (硬件所能访问) 的用 4 个字节保存(而不是一 位 bit)
一个变量的地址 - 用该变量首字节的地址表示. 这也就是为什么指针变量始终只占 4 字节的原因.
- # include <stdio.h>
- int main(void)
- {
- char ch = 'A';
- int i = 99;
- double x = 66.6;
- char * p = &ch;
- int * q = &i;
- double * r = &x;
- printf("%d %d %d\n", sizeof(p), sizeof(q), sizeof(r));
- return 0;
- }
5 - 动态内存分配
传统数组的缺点
数组长度必须事先指定, 而且只能是常整数, 不能是变量
例子 int a[5]; // 必须事先指定, 而且只能是常整数 int len = 5; int a[len];//error
2. 传统形式定义的数组, 该数组的内存程序员无法手动释放数组一旦定义.
系统为数组分配的内存空间就会一直存在, 除非数组所在的函数运行终止. 在一个函数运行期间, 系统为该函数中的数组分配的空间会一直存在. 直到该函数运行完毕时, 数组的空间才会被系统自动释放(不是清零).
例子: void f(void){ int a[5]={1,2,3,4,5};....}
// 数组 a 占 20 个字节的内存空间, 程序员无法手动编程释放它, 数组 a 只能在 f()函数结束被系统释放
3, 数组的长度一旦定义, 数组长度就不能再更改. 数组的长度不能在函数运行的过程中动态的扩充或缩小.
4, 传统方式定义的数组不能跨函数使用
A 函数定义的数组, 只有在 A 函数运行期间才可以被其他函数使用,
但 A 函数运行完毕后, A 函数中的数组将无法在被其他函数使用.
- # include <stdio.h>
- /*
- 代码实现(传统数组的缺陷)
- */
- void g(int * pArr, int len)
- {
- pArr[2] = 88; //pArr[2] == a[2]
- }
- void f(void)
- {
- int a[5] = {1,2,3,4,5}; //20 个字节的存储空间程序员无法手动编程释放它,
- // 它只能在本函数运行完毕时由系统自动释放
- g(a, 5);
- printf("%d\n", a[2]);
- }
- int main(void)
- {
- f();
- return 0;
- }
为什么需要动态分配内存
很好的解决的了传统数组的 4 个缺陷
6 - 动态内存分配举例_动态数组的构造
- /*
- 2009 年 11 月 17 日 10:21:31
- malloc 是 memory(内存) allocate(分配)的缩写
- */
- # include <stdio.h>
- # include <malloc.h> // 不能省
- int main(void)
- {
- int i = 5; // 分配了 4 个字节 静态分配 11 行
- int * p = (int *)malloc(4); //12 行
- /*
- 1. 要使用 malloc 函数, 必须添加 malloc.h 这个头文件
- 2. malloc 函数只有一个形参, 并且形参是整型
- 3. 4 表示请求系统为本程序分配 4 个字节
- 4. malloc 函数只能返回第一个字节的地址
- 5. 12 行分配了 8 个字节, p 变量占 4 个字节, p 所指向的内存也占 4 个字节
- 6. p 本身所占的内存是静态分配的, p 所指向的内存是动态分配的
- */
- *p = 5; //*p 代表的就是一个 int 变量, 只不过 * p 这个整型变量的内存分配方式和 11 行的 i 变量的分配方式不同
- free(p); //freep(p)表示把 p 所指向的内存给释放掉 p 本身的内存是静态的, 不能由程序员手动释放, p 本身的内存只能在 p 变量所在的函数运行终止时由系统自动释放
- printf("同志们好!\n");
- return 0;
- }
7-malloc 实现图解
- # include <stdio.h>
- # include <malloc.h>
- void f(int * q)
- {
- //*p = 200; //error
- //q = 200; //200 不是整型变量的地址, q 只能存放整型变量的地址
- //**q = 200; //error
- *q = 200;
- //free(q); // 把 q 所指向的内存释放掉 本语句必须的注释掉, 否则会导致第 20 行的代码出错
- }
- int main(void)
- {
- int * p = (int *)malloc(sizeof(int)); //sizeof(int)返回值是 int 所占的字节数
- *p = 10;
- printf("%d\n", *p); //10
- f(p); //p 是 int * 类型
- printf("%d\n", *p); //200 第 20 行
- return 0;
- }
8 - 动态一维数组示例
图解
- # include <stdio.h>
- # include <malloc.h>
- int main(void)
- {
- int a[5]; // 如果 int 占 4 个字节的话, 则本数组总共包含有 20 个字节, 每四个字节被当做了一个 int 变量来使用
- int len;
- int * pArr;
- int i;
- // 动态的构造一维数组
- printf("请输入你要存放的元素的个数:");
- scanf("%d", &len);
- pArr = (int *)malloc(4 * len); // 第 12 行 本行动态的构造了一个一维数组, 该一维数组的产度是 len, 该数组的数组名是 pArr, 该数组的每个元素是 int 类型 类似于 int pArr[len];
- // 对一维数组进行操作, 如: 对动态一维数组进行赋值
- for (i=0; i<len; ++i)
- scanf("%d", &pArr[i]);
- // 对位一维数组进行输出
- printf("一维数组的内容是:\n");
- for (i=0; i<len; ++i)
- printf("%d\n", pArr[i]);
- free(pArr); // 释放掉动态分配的数组
- return 0;
- }
9 - 使用动态数组的优点:
1. 动态数组长度不需要事先给定;
2. 内存空间可以手动释放.
3. 在程序运行中, 动态内存空间大小可以通过 realloc 函数手动扩充或缩小
10 - 静态内存和动态内存的比较
静态内存是由系统自动分配, 有系统自动释放
静态内存是在栈分配的
动态内存是由程序员手动分配, 手动释放
动态内存是在堆分配的
11 - 多级指针
- #### 多级指针代码实现
- # include <stdio.h>
- int main(void)
- {
- int i = 10;
- int * p = &i; //p 只能存放 int 类型变量的地址
- int ** q = &p; //q 是 int ** 类型, 所谓 int ** 类型就是指 q 只能存放 int * 类型变量的地址,
- int *** r = &q; //r 是 int *** 类型, 所谓 int *** 类型就是指 r 只能存放 int ** 类型变量的地址,
- //r = &p; //error 因为 r 是 int *** 类型, r 只能存放 int ** 类型变量的地址
- printf("i = %d\n", ***r); // 输出结果是 10 只有 ***r 才表示的是 i, *r 或 **r 或 ****r 代表的都不是 i
- return 0;
- }
多级指针代码实现 2
- #include <stdio.h>
- // 多级指针在函数中的应用
- void f(int ** q)
- {
- **q = 100; //*q 就是 p
- }
- void g()
- {
- int i = 10;
- int * p = &i;
- printf("i = %d *p = %d\n", i, *p);
- f(&p); //p 是 int * 类型 &p 就是 int ** 类型
- printf("i = %d *p = %d\n", i, *p);
- }
- int main(void)
- {
- g();
- return 0;
- }
12 - 静态变量不能跨函数使用
- /*
- 内存越界: 程序访问了一个不该被访问的内存, 函数内的静态空间, 不能被其他函数调用访问. 函数中的内存空间, 随函数终止而被释放. 内存空间释放后的内容不属于其他函数, 其他函数无权限访问. 但释放后的内存空间的地址是可以被其他函数读取的. 但指针变量可以存贮任何函数中静态内存空间的地址, p 都能存垃圾, p 想存谁存谁. 只是它此时已经没有权限读取(访问) i 这个地址的数据了, 出错..
- */
- # include <stdio.h>
- void f(int ** q) //q 是个指针变量, 无论 q 是什么类型的指针变量, 都只占 4 个字节
- {
- int i = 5;
- //*q 等价于 p q 和 **q 都不等价于 p
- //*q = i; //error 因为 * q = i; 等价于 p = i; 这样写是错误的
- *q = &i; // p = &i;
- }
- int main(void)
- {
- int *p; //13 行
- f(&p);
- printf("%d\n", *p); //16 行 本语句语法没有问题, 但逻辑上有问题
- return 0;
- }
13- 动态内存可以跨函数使用案例
- # include <stdio.h>
- # include <malloc.h>
- void f(int ** q) //*q 等价 p 已经声明了 q 的类型为 int **
- {
- *q = (int *)malloc(sizeof(int)); //sizeof(数据类型) 返回值是该数据类型所占的字节数
- // 等价于 p = (int *)malloc(sizeof(int));
- //q = 5; //error
- //*q = 5; //p = 5;
- **q = 5; //*p = 5;
- }
- int main(void)
- {
- int * p;
- f(&p); // 只有调用变量的地址, 才能改变变量的值
- printf("%d\n", *p);
- //f 函数中, 没有 free(q); 所以动态空间仍然保留, 动态空间中的内容可以被访问
- return 0;
- }
- ?
来源: http://www.bubuko.com/infodetail-3103489.html