【C语言】指针查漏补缺
- 预备知识
- 一维整数数组
- 字符数组
- 字符常量数组
- 字符串常量
- 二维数组
预备知识
-
sizeof 是计算对象或者类型创建的对象所占内存空间的大小,单位是字节
-
sizeof 是操作符,不是函数
-
strlen 求字符串长度的,计算的是字符串中\0之前出现的字符的个数
统计到\0为止,如果没有看到\0,会继续往后找 -
strlen 是库函数
size_t strlen ( const char * str );
关于数组名
数组名是数组首元素的地址
但是有2个例外:
- sizeof(数组名) - 数组名表示整个数组,计算的是整个数组的大小,单位是字节
- &数组名 - 数组名也表示整个数组,取出的是整个数组的地址
除了这个2个例外,所有的数组名都表示首元素的地址
一维整数数组
int main()
{
int a[] = { 1,2,3,4 };
int* p = a;
//int (*pa)[4] = &a;
printf("%p\n", p); 008FFCF4
printf("%p\n", p+1); 008FFCF8
printf("%p\n", pa); 008FFCF4
printf("%p\n", pa+1); 008FFD04
//a - int*
//&a - int (*)[4]
printf("%d\n", sizeof(a));//16,a作为数组名单独放在sizeof内部,计算的是数组的总大小,单位是字节
printf("%d\n", sizeof(a + 0));//a并非单独放在sizeof内部,也没有&,所以数组名a就是数组首元素的地址
//a+0还是数组首元素的地址,是地址大小就是 4/8 个字节
printf("%d\n", sizeof(*a));//a是首元素的地址,*a就是首元素,sizeof(*a)就算的就是首元素的大小 - 4
//a - int*
//*a - int
printf("%d\n", sizeof(a + 1));//a是首元素的地址,a+1是第二个元素的地址,sizeof(a+1)计算的是指针的大小 - 4/8
//a - int*
//a+1, 跳过一个int
printf("%d\n", sizeof(a[1]));//a[1]就是数组的第二个元素,sizeof(a[1])的大小 - 4个字节
printf("%d\n", sizeof(&a));//&a取出的数组的地址,数组的地址,也是地址呀,sizeof(&a)就是 4/8 个字节
printf("%d\n", sizeof(*&a));//&a是数组的地址,是数组指针类型,*&a是都数组指针解引用,访问一个数组的大小
//16字节
//sizeof(*&a) ==> sizeof(a) =16
printf("%d\n", sizeof(&a + 1));//&a数组的地址,&a+1跳过整个数组,&a+1还是地址,是 4/8 个字节
printf("%d\n", sizeof(&a[0]));//a[0]是数组的第一个元素,&a[0]是第一个元素的地址,是 4/8 个字节
printf("%d\n", sizeof(&a[0] + 1));//&a[0]是第一个元素的地址,&a[0]+1就是第二个元素的地址,是 4/8 个字节
//&a[0] - int*
//&a[0]+1 -> &a[1]
return 0;
}
字符数组
int main() {
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", strlen(arr));//随机值,arr是数组名,但是没有放在sizeof内部,也没&,arr就是首元素的地址
//strlen得到arr后,从arr数组首元素的地方开始计算字符串的长度,直到直到\0,但是arr数组中没有\0,arr内存的后边是否有\0,在什么位置
//是不确定的,所以\0之前出现了多少个字符是随机的。
//
printf("%d\n", strlen(arr + 0));//arr是数组首元素的地址,arr+0还是首元素的地址
//随机值
//printf("%d\n", strlen(*arr));//arr是数组首元素的地址,*arr 是首元素 - ‘a’ - 97
//strlen就把‘a’的ASCII码值 97 当成了地址
//err 会非法访问内存
//printf("%d\n", strlen(arr[1]));//arr[1] - 'b' - 98 - err
printf("%d\n", strlen(&arr));//随机值,&arr是数组的地址,数组的地址也是指向数组起始位置,和第一个案例一样
printf("%d\n", strlen(&arr + 1));//&arr + 1表示跳过这个数组,随机值
printf("%d\n", strlen(&arr[0] + 1));//第二个元素的地址,也是随机值
//printf("%d\n", sizeof(arr));//arr是数组名,并且是单独放在sizeof内部,计算的是数组总大小,单位是字节 - 6
//printf("%d\n", sizeof(arr + 0));//arr是数组名,并非单独放在sizeof内部,arr表示首元素的地址,arr+0还是首元素的地址
是地址大小就是4/8
//
printf("%d\n", sizeof(*arr));//arr是首元素的地址,*arr就是首元素,sizeof计算的是首元素的大小,是1字节
printf("%d\n", sizeof(arr[1]));//arr[1]是数组的第二个元素,sizeof(arr[1])计算的是第二个元素的大小,1个字节
printf("%d\n", sizeof(&arr));//&arr- 取出的是数组的地址,sizeof(&arr))计算的是数组的地址的大小,是地址就是4/8字节
printf("%d\n", sizeof(&arr + 1));//&arr是数组的地址,&arr+1跳过整个数组,指向'f'的后边,&arr+1的本质还是地址,是地址就是4/8字节
printf("%d\n", sizeof(&arr[0] + 1));//&arr[0]是‘a’的地址,&arr[0]+1是'b'的地址,是地址就是4/8字节
//&arr[0] - char*
}
字符常量数组
arr是存在栈上的数组,数据来自代码段的拷贝
int main()
{
char arr[] = "abcdef";
//char ch = 'w';
//int* pc = &ch;//char*
printf("%d\n", strlen(arr)); // 6
printf("%d\n", strlen(arr + 0)); //6 从a开始数
printf("%d\n", strlen(*arr)); //报错 把a的ascll码当地址传过去
printf("%d\n", strlen(arr[1])); // 报错
printf("%d\n", strlen(&arr)); // 6 &arr是数组的地址,数组的地址也是指向数组起始位置
printf("%d\n", strlen(&arr + 1)); // 随机
printf("%d\n", strlen(&arr[0] + 1)); // 5
//printf("%d\n", sizeof(arr)); 7
//printf("%d\n", sizeof(arr + 0)); //arr是数组首元素的地址,arr+0还是首元素的地址:4/8
//printf("%d\n", sizeof(*arr)); 1
//printf("%d\n", sizeof(arr[1])); 1
//printf("%d\n", sizeof(&arr)); 4/8
//printf("%d\n", sizeof(&arr + 1)); 4/8
//printf("%d\n", sizeof(&arr[0] + 1)); 4/8
return 0;
}
字符串常量
p用const修饰,在栈上,存储的是代码段常量字符串的首地址
int main()
{
const char* p = "abcdef";
const char** pp = &p;
printf("%d\n", strlen(pp)); // 6
printf("%d\n", strlen(p)); // 6
printf("%d\n", strlen(p + 1)); // 5
//printf("%d\n", strlen(*p)); //错误,把a的ASCLL码当成地址
//printf("%d\n", strlen(p[0])); //错误,同上
//strlen(&p)并不是一个有效的操作。&p得到的是指向指针变量p的指针,也就是一个指向指针的指针,
//而strlen需要的是一个指向有效字符串的指针。所以,这可能会导致未定义的行为
printf("%d\n", strlen(&p)); //随机值 &p是在p的那块内存往后找\0,位置不确定
printf("%d\n", strlen(&p + 1)); //随机值 同上
printf("%d\n", strlen(&p[0] + 1)); //5
printf("%zu\n", sizeof(p)); // 4/8
printf("%zu\n", sizeof(p + 1)); // 4/8
printf("%zu\n", sizeof(*p)); // 1 *p==*(p+0)==p[0]
printf("%zu\n", sizeof(p[0])); // 1
printf("%zu\n", sizeof(&p)); // 4/8
printf("%zu\n", sizeof(&p + 1)); // 4/8
printf("%zu\n", sizeof(&p[0] + 1)); // 4/8
return 0;
}
二维数组
int main()
{
//二维数组
int a[3][4] = { 0 };
printf("%d\n", sizeof(a));//a是二维数组的数组名,数组名单独放在sizeof内部,计算的是数组的总大小,单位是字节
//48
printf("%d\n", sizeof(a[0][0]));//a[0][0]是一个整型元素,大小是4个字节
printf("%d\n", sizeof(a[0]));//把二维数组的每一行看做一维数组的时候,a[0]是第一行的数组名,第一行的数组名单独放在sizeof内部
//计算的是第一行的总大小,单位是字节 - 16
printf("%d\n", sizeof(a[0] + 1));//a[0]虽然是第一行的数组名,但是并非单独放在sizeof内部
//a[0]作为第一行的数组名并非表示整个第一行这个数组,a[0]就是第一行首元素的地址,a[0]--> &a[0][0] - int*
//a[0]+1,跳过一个int,是a[0][1]的地址 4/8字节
printf("%d\n", sizeof(*(a[0] + 1)));//a[0]+1是第一行第二个元素的地址,所以*(a[0]+1)就是a[0][1],大小是4个字节
printf("%d\n", sizeof(a + 1));//a是二维数组的数组名,没单独放在sizeof内部,也没有&,所以a就是数组首元素的地址
//二维数组,我们把它想象成一维数组,它的第一个元素就是二维数组的第一行
//a就是第一行的地址,a+1 是第二行的地址,是地址,大小就是 4/8 个字节
//a - &a[0]
//a+1 - &a[1]
//a+2 - &a[2]
printf("%d\n", sizeof(*(a + 1)));//a+1是第二行的地址,*(a+1) 找到的就是第二行,sizeof(*(a + 1))计算的就是第二行的大小
//16
//*(a+1) --> a[1]
//sizeof(*(a + 1)) --> sizeof(a[1])
//
printf("%d\n", sizeof(&a[0] + 1));//&a[0]是第一行的地址,&a[0]+1就是第二行的地址,sizeof(&a[0] + 1)计算的第二行地址大小
//单位是字节 - 4/8
printf("%d\n", sizeof(*(&a[0] + 1)));//&a[0] + 1是第二行的地址,*(&a[0] + 1)拿到的就是第二行,大小就是16个字节
//*(&a[0]+1) --> a[1]
printf("%d\n", sizeof(*a));//a表示首元素的地址,就是第一行的地址 - &a[0]
//*a - 拿到的就是第一行 - 大小就是16个字节
//*a -> *(a+0) -> a[0]
//
printf("%d\n", sizeof(a[3]));//代码没问题
//a[3]是二维数组的第4行,虽然没有第四行,但是类型能够确定,大小就是确定的。大小就是一行的大小,单位是字节 - 16
//能够分析出 a[3]的类型是:int [4]
//
//任何一个表达式有2个属性
//3+5
//值属性:8
//类型属性:int
return 0;
}