C语言指针与数组
龙兆万 人气:01.概要复习
本篇的内容主要围绕指针与数组、指针与字符串等之间的关系,以及进一步理解sizeof 、strlen 的使用与意义。
数组是指具有相同类型元素的集合,字符串常量是一个指向在连续空间里存放的字符的首字符的地址的指针。我们会在下面理解数组与字符串数组的不同。
sizeof 是一个操作符,是计算类型空间大小的。strlen 是针对字符串的库函数,用来求字符串的长度。
对于数组来说,数组名都是首元素地址,除了这两种情况外:
- sizeof(数组名)——sizeof内部单独放置数组名表整个数组
- &数组名——对数组名进行取地址操作表取出整个数组的地址
2.指针与数组笔试题
我们的目的是求下面各个 printf 语句输出什么。
2.1一维数组
#include <stdio.h> int main() { int a[] = { 1,2,3,4 }; printf("%d\n", sizeof(a)); printf("%d\n", sizeof(a + 0)); printf("%d\n", sizeof(*a)); printf("%d\n", sizeof(a + 1)); printf("%d\n", sizeof(a[1])); printf("%d\n", sizeof(&a)); printf("%d\n", sizeof(*&a)); printf("%d\n", sizeof(&a + 1)); printf("%d\n", sizeof(&a[0])); printf("%d\n", sizeof(&a[0] + 1)); return 0; }
#include <stdio.h> int main() { int a[] = { 1,2,3,4 }; printf("%d\n", sizeof(a)); //sizeof 内部单独放置数组名,计算整个数组的大小,结果为 4(int类型)*4(元素个数)=16 printf("%d\n", sizeof(a + 0)); //sizeof 内部没有单独放置数组名,此时数组名表首元素地址,a+0 依然表示首元素地址 //故 sizeof 计算一个地址(指针)的大小为 4 or 8 printf("%d\n", sizeof(*a)); //sizeof 内部没有单独放置数组名,此时数组名表首元素地址,*a 表对该地址解引用 //即可得到 a 数组的第一个元素,大小为 4(int类型) printf("%d\n", sizeof(a + 1)); //sizeof 内部没有单独放置数组名,此时数组名表首元素地址,a+1 表第二个元素地址 //故 sizeof 计算一个地址(指针)的大小为 4 or 8 printf("%d\n", sizeof(a[1])); //sizeof 内部没有单独放置数组名,a[1] 表数组的第二个元素,此写法可转置为 *(a+1) //即计算的结果是第二个元素的类型大小,即 4(int类型) printf("%d\n", sizeof(&a)); //sizeof 内部没有单独放置数组名,而是放置了 &a ,表取出整个数组的地址 //故 sizeof 计算一个地址(指针)的大小为 4 or 8 printf("%d\n", sizeof(*&a)); //sizeof 内部虽然看似没有单独放置数组名,但是通过 &a 取出整个数组地址 //然后通过 * 找到整个地址,与 sizeof(a) 没有差别 //故这里的 sizeof 计算的也是整个数组的大小,即 4(int类型)*4(元素个数)=16 printf("%d\n", sizeof(&a + 1)); //sizeof 内部没有单独放置数组名,&a+1 表取出整个数组的地址,并向后跳跃一个数组类型的大小 //即此时的地址是指向元素 4 的后面一块 int 类型的内存空间 //故 sizeof 计算地址(指针)的大小为 4 or 8 printf("%d\n", sizeof(&a[0])); //sizeof 内部没有单独放置数组名,&a[0] 表取出数组第一个元素的地址 //故 sizeof 计算地址(指针)的大小为 4 or 8 printf("%d\n", sizeof(&a[0] + 1)); //sizeof 内部没有单独放置数组名,&a[0]+1 表取出数组第一个元素地址并向后跳跃一个 int 类型的大小 //故 sizeof 计算的是地址(指针)的大小为 4 or 8 return 0; }
2.2字符数组
#include <stdio.h> int main() { char arr[] = { 'a','b','c','d','e','f' }; printf("%d\n", sizeof(arr)); printf("%d\n", sizeof(arr + 0)); printf("%d\n", sizeof(*arr)); printf("%d\n", sizeof(arr[1])); printf("%d\n", sizeof(&arr)); printf("%d\n", sizeof(&arr + 1)); printf("%d\n", sizeof(&arr[0] + 1)); printf("%d\n", strlen(arr)); printf("%d\n", strlen(arr + 0)); printf("%d\n", strlen(*arr)); printf("%d\n", strlen(arr[1])); printf("%d\n", strlen(&arr)); printf("%d\n", strlen(&arr + 1)); printf("%d\n", strlen(&arr[0] + 1)); return 0; }
#include <stdio.h> int main() { char arr[] = { 'a','b','c','d','e','f' };//非字符串!!!!!! printf("%d\n", sizeof(arr)); //sizeof 内部单独放置数组名,故计算的是整个数组的大小 //即大小为 1(char类型)*6(元素个数)=6 printf("%d\n", sizeof(arr + 0)); //sizeof 内部没有单独放置数组名,arr 表数组首元素地址,arr+0 亦表示首元素地址 //故 sizeof 计算地址(指针)的大小为 4 or 8 printf("%d\n", sizeof(*arr)); //sizeof 内部没有单独放置数组名,arr 表数组首元素地址,*arr 表对该地址解引用 //便得到数组第一个元素 'a' ,故大小为 1(char类型) printf("%d\n", sizeof(arr[1])); //sizeof 内部没有单独放置数组名,arr[1] 表数组第二个元素,即 'b' //故大小为 1(char类型) printf("%d\n", sizeof(&arr)); //sizeof 内部没有单独放置数组名,&arr 表取出整个数组的地址 //故 sizeof 计算的地址(指针)的大小为 4 or 8 printf("%d\n", sizeof(&arr + 1)); //sizeof 内部没有单独放置数组名,&arr+1 表取出整个数组的地址并向后跳跃一个数组类型的大小 //即指向元素 'f' 后一个 char 类型的空间 //故 sizeof 计算的是地址(指针)的大小为 4 or 8 printf("%d\n", sizeof(&arr[0] + 1)); //sizeof 内部没有单独放置数组名,&arr[0]+1 表取出数组一个元素的地址并向后跳跃一个 char 类型的大小 //故 sizeof 计算的地址(指针)的大小为 4 or 8 printf("%d\n", strlen(arr)); //strlen 不是 sizeof ,故 arr 表数组首元素地址,strlen 会以此地址开始向后寻找 '\0' //但此数组并不包含 '\0' ,故 strlen 的返回值为随机值 printf("%d\n", strlen(arr + 0)); //strlen 不是 sizeof ,arr+0 表首元素地址,strlen 的返回值也是一个随机值 printf("%d\n", strlen(*arr)); //strlen 不是 sizeof ,*arr 表对数组首元素地址解引用,得到字符 'a' ,其 ASCLL 码值为 97 //strlen 会以 97 为起始地址向后寻找 '\0' //但个人理解认为:97 是一个没有初始化的指针(地址),即野指针 //故这里会报错 printf("%d\n", strlen(arr[1])); //arr[1] 表数组的第二个元素 'b' ,其 ASCLL 码值为 98 //strlen 会以 98 为起始地址向后寻找 '\0' //但个人理解认位:98 是一个没有初始化的指针(地址),即野指针 //故这里依然会报错 printf("%d\n", strlen(&arr)); //&arr 表取出整个数组的地址,strlen 会以此作为起始地址向后寻找 '\0' //这里需要注意,在 strlen 的函数声明当中,参数是一个 char* 类型的指针 //也就是我们虽然传参时传了一个数组指针,但是 strlen 接收时会自动强转为 char* 类型指针 //故数组并不包含 '\0' ,strlen 的返回值是一个随机值 printf("%d\n", strlen(&arr + 1)); //&arr+1 表取出整个数组的地址并向后跳跃一个数组类型的大小 //即指向了元素 'f' 的后一块 char 类型的空间 //strlen 会以此地址作为起始地址向后寻找 '\0' //但因我们无法确定 '\0' 的位置,所以 strlen 的返回值是一个随机值 printf("%d\n", strlen(&arr[0] + 1)); //&arr[0]+1 表取出数组首元素地址并向后跳跃一个 char 类型的大小 //即指向了元素 'b' ,strlen 会以此地址作为起始地址向后寻找 '\0' //但数组中并不包含 '\0' ,故 strlen 的返回值是一个随机值 return 0; }
我们把报错的两条语句注释掉:
2.3字符串数组
#include <stdio.h> int main() { char arr[] = "abcdef"; printf("%d\n", sizeof(arr)); printf("%d\n", sizeof(arr + 0)); printf("%d\n", sizeof(*arr)); printf("%d\n", sizeof(arr[1])); printf("%d\n", sizeof(&arr)); printf("%d\n", sizeof(&arr + 1)); printf("%d\n", sizeof(&arr[0] + 1)); printf("%d\n", strlen(arr)); printf("%d\n", strlen(arr + 0)); printf("%d\n", strlen(*arr)); printf("%d\n", strlen(arr[1])); printf("%d\n", strlen(&arr)); printf("%d\n", strlen(&arr + 1)); printf("%d\n", strlen(&arr[0] + 1)); return 0; }
#include <stdio.h> int main() { char arr[] = "abcdef";//注意数组里面放的是字符串!!! printf("%d\n", sizeof(arr)); //sizeof 内部单独放置数组名,即计算整个数组的大小 //大小为 1(char类型)*7(元素个数)=7 printf("%d\n", sizeof(arr + 0)); //sizeof 内部没有单独放置数组名,arr+0 表数组首元素地址 //故 sizeof 计算地址(指针)的大小为 4 or 8 printf("%d\n", sizeof(*arr)); //sizeof 内部没有单独放置数组名,*arr 表对数组首元素地址解引用 //即得到字符 'b' ,故大小为 1(char类型) printf("%d\n", sizeof(arr[1])); //sizeof 内部没有单独放置数组名,arr[1] 表数组第二个元素即字符 'b' //故大小为 1(char类型) printf("%d\n", sizeof(&arr)); //sizeof 内部没有单独放置数组名,&arr 表对整个数组取地址 //故 sizeof 计算的地址(指针)大小为 4 or 8 printf("%d\n", sizeof(&arr + 1)); //sizeof 内部没有单独放置数组名,&arr+1 表取出整个数组地址并向后跳跃一个数组类型大小 //即指向元素 '\0' 后面一块数组类型大小的空间 //故 sizeof 计算的地址(指针)大小为 4 or 8 printf("%d\n", sizeof(&arr[0] + 1)); //sizeof 内部没有单独放置数组名,&arr[0]+1 表取出数组首元素地址并向后跳跃一个 char 类型大小 //即指向数组第二个元素 //故 sizeof 计算的地址(指针)大小为 4 or 8 printf("%d\n", strlen(arr)); //strlen 不是 sizeof ,arr 表数组首元素地址,strlen 会以此为起始地址向后寻找 '\0' //因为此数组包含 '\0' ,故 strlen 的返回值为 6 printf("%d\n", strlen(arr + 0)); //arr+0 表数组首元素地址,strlen 会以此地址为起始地址向后寻找 '\0' //因为此数组包含 '\0' ,故 strlen 的返回值为 6 printf("%d\n", strlen(*arr)); //*arr 表对数组首元素地址解引用,得到字符 'a' ,其 ASCLL 码值为 97 //故 strlen 会以此地址为起始地址向后寻找 '\0' //但个人理解认位: 97 是一个没有初始化的指针(地址),即野指针 //故这里会报错 printf("%d\n", strlen(arr[1])); //arr[1] 表数组第二个元素,即字符 'b' ,其 ASCLL 码值为 98 //strlen 会以此地址会起始地址向后寻找 '\0' //但个人理解认位:98 是一个没有初始化的指针(地址),即野指针 //故这里会报错 printf("%d\n", strlen(&arr)); //&arr 表取出整个数组的地址,但观察 strlen 函数的声明可以发现 //strlen 的函数参数是一个 char* 类型的指针 //即我们传参传进去的是一个数组指针,当 strlen 接收的时候,会将其强转为字符指针 //故 strlen 会以数组首元素地址为起始地址,向后寻找 '\0' //因为数组包含 '\0' ,故 strlen 的返回值为 6 printf("%d\n", strlen(&arr + 1)); //&arr+1 表取出整个数组的地址并向后跳跃一个数组类型大小 //即指向 '\0' 后一块数组类型的空间 //strlen 会以此地址为起始地址向后寻找 '\0' //但我们无法确定 '\0' 的具体位置,所以 strlen 会返回一个随机值 printf("%d\n", strlen(&arr[0] + 1)); //&arr[0]+1 表取出数组首元素地址并向后跳跃一个 char 类型的大小 //即指向了数组第二个元素的地址 //strlen 会以此地址作为起始地址向后寻找 '\0' //故 strlen 的返回值为 5 return 0; }
我们把报错的语句注释掉:
2.4字符串指针
#include <stdio.h> int main() { char* p = "abcdef";//p 变量存放的是字符串常量的首元素地址 printf("%d\n", sizeof(p)); //sizeof 计算地址(指针)的大小为 4 or 8 printf("%d\n", sizeof(p + 1)); //p+1 表字符串常量的第二个元素的地址 //故 sizeof 计算的地址(指针)的大小为 4 or 8 printf("%d\n", sizeof(*p)); //*p 表对字符串常量首元素地址解引用。得到字符 'a' //故计算的大小为 1(char类型) printf("%d\n", sizeof(p[0])); //p[0] 可改写成 *(p+0) ,表对字符串常量首元素地址解引用,得到字符 'a' //即计算的大小为 1(char类型) printf("%d\n", sizeof(&p)); //&p 表对 char* 类型指针取地址 //故 sizeof 计算的地址(指针)的大小为 4 or 8 printf("%d\n", sizeof(&p + 1)); //&p+1 表对 char* 类型指针变量 p 取地址并向后跳跃一个 char 类型的大小 //指向的空间是未知的 //但 sizeof 计算的地址(指针)的大小为 4 or 8 printf("%d\n", sizeof(&p[0] + 1)); //&p[0]+1 可改写成 &(*(p+0))+1 ,表取出字符串常量的首元素地址并向后跳跃一个 char 类型的大小 //即指向了字符 'b' //故 sizeof 计算的地址(指针)的大小为 4 or 8 printf("%d\n", strlen(p)); //strlen 不是 sizeof ,p表字符串首元素地址 //strlen 以此地址为起始地址,向后寻找 '\0' //故 strlen 的返回值为 6 printf("%d\n", strlen(p + 1)); //p+1 表字符串常量的第二个元素的地址,strlen 会以此为地址向后寻找 '\0' //故 strlen 的返回值为 5 printf("%d\n", strlen(*p)); //*p 表字符串常量首元素,即字符 'a',strlen 会以此为地址向后寻找 '\0' //但个人理解人为:'a' 的 ASCLL 码值为 97 ,但 97 是一个没有被初始化的指针(地址),即野指针 //故这里会报错 printf("%d\n", strlen(p[0])); //p[0] 可改写成 *(p+0) ,表字符串常量首元素,即字符 'a',其 ASCLL 码值为 97 //strlen 会以此地址为起始地址向后寻找 '\0' //但个人理解认位:97 是一个没有被初始化的指针(地址),即野指针 //故这里会报错 printf("%d\n", strlen(&p)); //&p 表取出 char* 类型指针变量的地址,strlen 以此地址为起始地址向后寻找 '\0' //但我们无法确定 '\0' 的位置 //故 strlen 会返回一个随机值 printf("%d\n", strlen(&p + 1)); //&p+1 表取出 char* 类型指针变量的地址并向后跳跃一个 char 类型的大小,strlen 以此地址为起始地址向后寻找 '\0' //但我们无法确定 '\0'的位置,故 strlen 的返回值会返回一个随机值 printf("%d\n", strlen(&p[0] + 1)); //*p{0}+1 可改写为 &(*(p+0))+1 ,表取出字符串常量首元素地址并向后跳跃一个 char 类型的大小 //即指向了字符串常量的第二个元素的地址,strlen 会以此地址为起始地址向后寻找 '\0' //故 strlen 的返回值为 5 return 0; }
我们对报错的两条语句注释:
2.5二维数组
#include <stdio.h> int main() { int a[3][4] = { 0 }; printf("%d\n", sizeof(a)); //sizeof 内部放置的是数组名,故计算整个数组的大小 //即大小为 4(int类型)*12(元素个数)=48 printf("%d\n", sizeof(a[0][0])); //sizeof 内部没有单独放置数组名,a[0][p] 表数组首元素 //故 sizeof 的计算值为 4(int类型) printf("%d\n", sizeof(a[0])); //sizeof 内部放置的看似不是单独的数组名,但 a[0] 表数组的第一个元素 //此元素也是一个数组 //故 sizeof 计算的大小为 4(int类型)*4(元素个数)=16 printf("%d\n", sizeof(a[0] + 1)); //sizeof 内部放置的不是单独的数组名,a[0] 表二维数组的第一个元素,即拿到了一个数组 //a[0]+1 表二维数组的第一个数组元素的地址并向后跳跃一个 int 类型的大小 //故 sizeof 计算的地址(指针)的大小为 4 or 8 printf("%d\n", sizeof(*(a[0] + 1))); //*(a[0]+1) 表对二维数组的第一个数组元素的地址并向后跳跃一个 int 类型的大小的解引用 //即找到了二维数组中的某一个元素 //故 sizeof 计算的大小为 4(int类型) printf("%d\n", sizeof(a + 1)); //sizeof 内部没有单独放置数组名,a+1 表二维数组的首元素地址并向后跳跃一个一维数组类型的大小 //即指向了二维数组的第二个数组元素的地址 //故 sizeof 计算的地址(指针)的大小为 4 or 8 printf("%d\n", sizeof(*(a + 1))); //*(a+1) 表对二维数组的首元素地址并向后跳跃一个一维数组类型的大小的解引用 //即得到了二维数组的第二个元素 //此元素为一个一维数组,故 sizeof 计算的大小为 4(int类型)*4(元素个数)=16 printf("%d\n", sizeof(&a[0] + 1)); //&a[0]+1 表二维数组的首元素取地址并向后跳跃一个一维数组的类型大小 //指向了二维数组的第二个元素 //故 sizeof 计算的地址(指针)的大小为 4 or 8 printf("%d\n", sizeof(*(&a[0] + 1))); //*(&a[0]+1) 表对二维数组的首元素取地址并向后跳跃一个一维数组的类型大小的解引用 //即得到了二维数组的第二个元素 //此元素是一个一维数组,故 sizeof 计算的大小的 4(int类型)*4(元素个数)=16 printf("%d\n", sizeof(*a)); //sizeof 内部没有单独放置数组名,*a 表对二维数组首元素地址解引用 //得到一个一维数组 //故 sizeof 计算的大小为 4(int类型)*4(元素个数)=16 printf("%d\n", sizeof(a[3])); //a[3] 表二维数组的第三个元素 //需要说明的是,sizeof 只对类型感兴趣 //也就是说,二维数组虽然不存在第三个元素,但它的类型依旧是二维数组 //故 sizeof 计算的是 4(int类型)*4(元素个数)=16 return 0; }
加载全部内容