编程随闪--指针(三)

系列文章:

编程随闪--指针(一)

编程随闪--指针(二)


1.sizeof单目操作符和strlen()函数

1.1sizeof单目操作符

        在学习操作符的时候,我们学习了 sizeof 操作符sizeof 操作符是计算变量所占内存空间大小的,单位是字节,如果操作数是类型,则计算的是使用该类型创建的变量所占内存空间的大小。即sizeof操作符只能用来计算占用内存空间的大小(单位:字节),不关心内存空间中的值。

//sizeof是单目操作符,并不是函数,因为函数的调用必须要带括号

#include<stdio.h>

int main()
{
	int a = 10;
	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof a);// sizeof 操作符是计算变量所占内存空间大小的,单位是字节
	printf("%d\n", sizeof(int));//操作数是类型,则计算的是使用该类型创建的变量所占内存空间的大小。

	return 0;
}

1.2strlen()函数

        strlen()是C语言库函数,该函数是求字符串长度的。

//函数原型:
size_t strlen ( const char * str );

        strlen()库函数所需头文件为<string.h>,统计的是从strlen()函数的参数str这个地址开始,向后一直到 '\0' 之前字符串中字符的个数。strlen()库函数会一直向后找 '\0' 字符,直到找到为止。所以可能存在越界查找。

#include <stdio.h>
int main()
{
     char arr1[3] = {'a', 'b', 'c'};
     char arr2[] = "abc";
     printf("%d\n", strlen(arr1));//随机值,没有\0
     printf("%d\n", strlen(arr2));//3
 
     printf("%d\n", sizeof(arr1));//3
     printf("%d\n", sizeof(arr2));//4,'\0'也占一字节
     return 0;
}
#include<stdio.h>

int main()
{
	int a = 8;
	short s = 4;
	printf("%d\n", sizeof(s = a + 2));//2 short类型大小
    /*
        为什么sizeof中表达式不参与计算?
            C语言是编译型语言,要经过编译,链接,运行
            表达式执行是在运行时实现的,而sizeof操作符则是在编译时执行的,编译时其表达式取决于s的类型short,则直接换成short所占字节大小2字节
    */
	printf("%d\n", s);//4
	

	return 0;
}
sizeof单目操作符(不关注内存中的内容)strlen()函数(会关注内存中的内容)

1,sizeof是操作符

2,sizeof计算操作数所占内存的

大小,单位是字节

3,不关注内存中存放什么数据

4,sizeof括号中有表达式,表达式不参与计算

1,strlen()是库函数,需要包含头文件<string.h>才能使用

2,strlen()函数是求字符串长度的函数,统计的是 ‘\0’ 之前字符的个数

3,该函数会关注字符串中是否有 '\0' ,如果没有 '\0' ,会持续往后找。可能会造成越界

2.数组和指针相关题解析

2.1一维数组

#include<stdio.h>
/*
sizeof操作符
*/
int main()
{
	int a[] = { 1,2,3,4 };//4个元素
	printf("%d\n", sizeof(a));//16字节
	//sizeof(a)就是sizeof(数组名)的场景
	printf("%d\n", sizeof(a + 0));//4字节或8字节
	//sizeof(a + 0)不是sizeof(数组名),则表示首元素地址,类型int*,a+0还是首元素的地址
	printf("%d\n", sizeof(*a));//4字节
	//a是首元素地址,*a为首元素,大小就是4字节。*a == a[0] == *(a + 0)
	printf("%d\n", sizeof(a + 1));
	//不符合特例,则a表示首元素地址,类型为int* ,a+1跳过一个整型,表示第二个元素的地址
	printf("%d\n", sizeof(a[1]));//第二个元素,大小4字节
	printf("%d\n", sizeof(&a));//4/8字节
	//&a是整个数组的地址,数组地址也是一个地址,是地址就是4/8字节
	printf("%d\n", sizeof(*&a));//16字节
	//1,*、&抵消,sizeof(a),整个数组大小
	//2,&a取整个数组的地址,类型是int (*)[4],*解引用后,访问的是整个数组
	printf("%d\n", sizeof(&a + 1));//4/8字节
	//&a整个数组地址,&a + 1是跳过整个数组后那个位置的地址
	printf("%d\n", sizeof(&a[0]));
	//取首元素地址,地址大小4/8字节
	printf("%d\n", sizeof(&a[0] + 1));//数组第二个元素的地址,大小4/8字节

	return 0;
}

2.2字符数组

#include<stdio.h>
/*
sizeof操作符
*/
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", sizeof(arr));//6字节
	//数组名单独放在sizeof内部,计算的是整个数组的大小 
	printf("%d\n", sizeof(arr + 0));//4/8字节
	//arr是数组名,表示首元素的地址,arr+0还是首元素的地址
	printf("%d\n", sizeof(*arr));//1字节
	//arr首元素地址,*arr表示首元素,大小为1字节
	printf("%d\n", sizeof(arr[1]));//第二个元素的值,大小1字节
	printf("%d\n", sizeof(&arr));//4/8字节
	//&arr整个数组的地址,
	printf("%d\n", sizeof(&arr + 1));//4/8字节
	//&arr整个数组的地址,+1跳过整个数组,指向数组后面的空间
	printf("%d\n", sizeof(&arr[0] + 1));//4/8字节
	//&arr[0]取数组第一个元素的地址,+1取第二个元素的地址
	return 0;
}
#include<stdio.h>
#include<string.h>

/*
strlen()函数
*/
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", strlen(arr));//随机值
	//arr首元素地址,strlen()从首地址开始数,但数组中没有'\0'会越界访问,结果是随机的(什么时候遇到\0未知)
	printf("%d\n", strlen(arr + 0));//随机值
	//arr是首元素地址,+0还是首元素地址,但没有'\0',会越界访问,结果随机
	printf("%d\n", strlen(*arr));//err,错误代码,越界访问
	//arr是首元素地址,*解引用后为首元素'a','a'的ASCII码值为97,将97视为地址传递给strlen().strlen()得到的就是野指针
	printf("%d\n", strlen(arr[1]));//err,错误代码
	//数组第二个元素b,'b'的ASCII码为98,strlen()得到野指针
	printf("%d\n", strlen(&arr));//随机值
	//&arr整个数组的地址,起始和首元素地址相同
	printf("%d\n", strlen(&arr + 1));//随机值
	//&arr整个数组的地址,+1跳过整个数组后开始向后访问,和&arr差6长度
	printf("%d\n", strlen(&arr[0] + 1));//随机值
	//&arr[0]取首元素地址,+1为第二个元素的地址。从第二个元素地址开始向后数,比arr差1

	return 0;
}

#include<stdio.h>

int main()
{
	//字符串末尾自动放置'\0'
	char arr[] = "abcdef";
	printf("%d\n", sizeof(arr));//7字节
	//
	printf("%d\n", sizeof(arr + 0));//4/8字节
	//arr首元素地址,+0还是首元素地址
	printf("%d\n", sizeof(*arr));//1字节
	//arr首元素地址,*arr是首元素
	printf("%d\n", sizeof(arr[1]));//1字节,第二个元素
	printf("%d\n", sizeof(&arr));//4/8字节
	//&arr是整个数组的地址,是地址就是4/8字节
	printf("%d\n", sizeof(&arr + 1));//4/8字节
	//&arr是数组地址,+1跳过整个数组,指向数组后的地址
	printf("%d\n", sizeof(&arr[0] + 1));//4/8字节
	//&arr[0]首元素地址,+1为第二个元素的地址

	return 0;
}
#include<stdio.h>
#include<string.h>

int main()
{
	//字符串末尾自动放置'\0'
	char arr[] = "abcdef";
	printf("%d\n", strlen(arr));//6字节
	//arr首元素地址,则从首元素开始数,遇到'\0'停止
	printf("%d\n", strlen(arr + 0));//6字节
	//arr首元素地址,+0还是首元素地址
	//printf("%d\n", strlen(*arr));//err错误代码
	//arr首元素地址,*解引用为首元素,'a'ASCII为97,越界访问
	//printf("%d\n", strlen(arr[1]));//err错误代码
	//第二个元素本身,'b'ASCII码为98,越界访问
	printf("%d\n", strlen(&arr));//6字节
	//&arr是数组地址,和首元素地址起始相同,遇到'\0'停止
    //&arr数组地址类型为char (*)[7]
    //size_t strlen(const char* c1);类型不匹配
	printf("%d\n", strlen(&arr + 1));//随机值
	//&arr数组地址,+1跳过整个数组向后访问,为随机值
	printf("%d\n", strlen(&arr[0] + 1));//5字节
	//&arr[0]首元素地址,+1第二个元素地址,

	return 0;
}
#include<stdio.h>

int main()
{
	//字符串末尾自动放置'\0'
	//指针变量p指向常量字符串,常量字符串不可修改,加const更严谨
	const char* p = "abcdef";
	printf("%d\n", sizeof(p));//4/8字节
	//p指针变量存储首元素地址
	printf("%d\n", sizeof(p + 1));//4/8字节
	//p+1第二个元素地址
	printf("%d\n", sizeof(*p));//1字节
	//p首元素地址,*解引用后为a本身
	printf("%d\n", sizeof(p[0]));//1字节,首元素本身
	printf("%d\n", sizeof(&p));//4/8字节
	//&p二级指针,存储p指针变量地址
	printf("%d\n", sizeof(&p + 1));//4/8字节
	printf("%d\n", sizeof(&p[0] + 1));//4/8字节
	//&p[0]

	return 0;
}
#include<stdio.h>
#include<string.h>

int main()
{
	//字符串末尾自动放置'\0'
	//指针变量p指向常量字符串,常量字符串不可修改,加const更严谨
	const char* p = "abcdef";
	printf("%d\n", strlen(p));//6字节
	//p存首字符地址,遇到'\0'停止
	printf("%d\n", strlen(p + 1));//5字节
	//p+1第二个元素地址
	//printf("%d\n", strlen(*p));//error错误代码
	//*p第一个元素本身,'a'ASCII 97,会报错,越界访问
	//printf("%d\n", strlen(p[0]));//error
	//同上
	printf("%d\n", strlen(&p));//所求值和字符串没关系,为随机值
	//p指针变量为一级指针,&p为二级指针,是p的地址,strlen()从p指针变量起始位置向后数(p变量存放地址不知道)
	printf("%d\n", strlen(&p + 1));//随机值,和字符串没关系,和&p也没关系
	//
	printf("%d\n", strlen(&p[0] + 1));//5字节
	//&p[0]是首字符a地址,+1第二个字符b地址

	return 0;
}

2.3二维数组

#include<stdio.h>
//数组名的理解,除了俩个例外,其余情况下,数组名表示数组首元素的地址,那么是哪俩种例外呢?
//1,sizeof(数组名):数组名表示整个数组,计算的是整个数组元素所占内存字节大小
//2,&数组名:数组名表示整个数组,取的是整个数组的地址

int main()
{
	int a[3][4] = { 0 };
	printf("%d\n", sizeof(a));//12*4 = 48字节
	//a是二维数组数组名,sizeof(数组名)表示整个二维数组的大小
	printf("%d\n", sizeof(a[0][0]));//4字节
	//a[0][0]第一行第一列元素本身
	printf("%d\n", sizeof(a[0]));//4*4 = 16字节
	//a[0]第一行的数组名,数组名单独放在sizeof()内部,计算的是第一行一维数组的总大小
	printf("%d\n", sizeof(a[0] + 1));//4/8字节
	//a[0]是第一行一维数组的数组名,表示一维数组首元素地址,+1表一维数组第二个元素地址
	printf("%d\n", sizeof(*(a[0] + 1)));//4字节
	//a[0]第一行一维数组的数组名,表一维数组首元素地址,+1为第二个元素地址,*解引用为一维数组第二个元素,大小为其类型int
	printf("%d\n", sizeof(a + 1));//4/8字节
	//a为二维数组数组名,表示二维数组首元素地址,+1表二维数组第二个元素地址(第二行一维数组int(*)[4])
	printf("%d\n", sizeof(*(a + 1)));//4*4 = 16字节
	//访问第二行一维数组,*(a+1) 等价于a[1],为第二行数组名
	printf("%d\n", sizeof(&a[0] + 1));//4/8字节
	//&a[0]是二维数组首元素的地址(第一行一维数组的数组地址) +1是第二个元素的地址(第二行一维数组的数组地址)
	printf("%d\n", sizeof(*(&a[0] + 1)));//4*4 = 16字节
	//二维数组第二个元素的地址解引用后访问第二行一维数组
	//&a[0]等价于a,(&a[0] + 1)等价于(a + 1)
	printf("%d\n", sizeof(*a));//4*4 = 16字节
	//a数组名表示二维数组首元素地址,解引用后访问首元素(第一行一维数组整行)
	//*a等价于*(a+0)等价于a[0]
	printf("%d\n", sizeof(a[3]));//4 * 4 = 16字节
	//sizeof()不看内容,只计算大小,虽然a[3]不存在,但是通过类型的推断是可以算出字节大小
	//a[3]是第四行数组名,单独放在sizeof内部,计算的是第四行的大小
	return 0;
}

2.4指针

#include<stdio.h>

int main()
{
	//结果是?
	int a[5] = { 1, 2, 3, 4, 5 };
	int* ptr = (int*)(&a + 1);
	//&a表数组地址,类型为int(*)[5],+1还是数组地址,与int*类型不同,故需要强制类型转换
	//ptr指针变量存储的是跳过整个数组后的地址
	printf("%d,%d", *(a + 1), *(ptr - 1));
	//a数组名表示数组首元素地址,+1为第二个元素的地址,解引用后为第二个元素2
	//ptr指针变量存储的是数组地址之后的地址,类型为int*,-1回退4字节,指向5的地址,解引用后为5
	return 0;
}
#included<stdio.h>

//在X86环境下
//假设结构体的⼤⼩是20个字节
//程序输出的结果是啥?
struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}*p = (struct Test*)0x100000;//结构体指针变量存放地址

int main()
{
	//结果是?
	printf("%p\n", p + 0x1);// 00100014
	//p+1跳过一个结构体指针,+20字节
	printf("%p\n", (unsigned long)p + 0x1);//0x100000 + 0x1 == (00100001)
	//unsigned long无符号长整型,p不是指针了,则其内容就是整型了,+1就是+1
	printf("%p\n", (unsigned int*)p + 0x1);//0x100004(00100004)
	//unsigned int*无符号整型指针,+1跳过一个无符号整型类型,+4字节
	return 0;
}
#include<stdio.h>

int main()
{
	//结果是?
	int a[3][2] = { (0, 1), (2, 3), (4, 5) };
	//(0, 1)为逗号表达式,从左向右 依次计算,最后一个表达式结果为整个表达式结果
	//a[3][2] = {1,3,5}为{1,3,5,0,0,0};
	int* p;
	p = a[0];//p存储二维数组首元素地址(一维数组数组地址int(*)[2])
	printf("%d", p[0]);//1
	//p[0]等价于*(p + 0)等价于*(a[0] + 0)等价于*(*(a + 0) + 0),即访问第一行第一列,为1
	return 0;
}
#include<stdio.h>

int main()
{
	////假设环境是x86环境,程序输出的结果是啥?
	int a[5][5];
	int(*p)[4];//p指针变量为数组指针,p类型是int(*)[4]
	p = a;//a数组名表示首元素地址(第一行一维数组的数组地址,a类型为int (*)[5])
	//上行代码会有警告,因为有类型差异
	printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);//-4地址形式(FFFFFFFC),-4整型形式(-4)
	//指针-指针的绝对值得到指针之间的元素个数(小地址 - 大地址为负数)
	//-4补码为FFFFFFFC
	return 0;
}

    

                                                                图2.1&p[4][2]与&a[4][2]

#include<stdio.h>

int main()
{
	////假设环境是x86环境,程序输出的结果是啥?
	int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int* ptr1 = (int*)(&aa + 1);//数组指针类型强制类型转换为整型指针
	//&arr取整个二维数组的数组地址,+1跳过整个二维数组,指向二维数组后的地址
	int* ptr2 = (int*)(*(aa + 1));//(int*)可以不带,类型都相同,没必要强制类型转换
	//aa二维数组名表示二维数组首元素地址(第一行一维数组数组地址,int(*)[5]),+1表示二维数组第二个元素地址(第二行一维数组的数组地址)
	//解引用之后为第二行一维数组的数组名,表示一维数组的首元素地址,即6的地址
	printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));//10,5
	return 0;
}
#include<stdio.h>

int main()
{
	////假设环境是x86环境,程序输出的结果是啥?
	char* a[] = { "work","at","alibaba" };//字符指针数组
	char** pa = a;//a数组名表示数组首元素地址
	pa++;//到第二个数组元素"at"的地址
	printf("%s\n", *pa);//at
	return 0;
}

                  

                                                        图2.2字符指针数组

#include<stdio.h>

int main()
{
	////假设环境是x86环境,程序输出的结果是啥?
	char* c[] = { "ENTER","NEW","POINT","FIRST" };
	char** cp[] = { c + 3,c + 2,c + 1,c };
	char*** cpp = cp;//数组名表首元素地址
	printf("%s\n", **++cpp);//POINT
	//cpp原指向cp数组首元素c+3的地址,自增后指向c+2的地址,对其解引用得到c+2,再解引用得到c+2中的值(首字符地址)
	printf("%s\n", *-- * ++cpp + 3);//ER
	//cpp指向c+2的地址,自增指向c+1的地址,解引用得到c+1,自减指向c,对c解引用得到c中的值(首字符地址),+3得到之后的er
	printf("%s\n", *cpp[-2] + 3);//ST
	//cpp[-2]等价于*(cpp-2),cpp指向c+1的地址,-2得到c+3的地址,解引用得到c+3,再对c+3解引用得到c+3中的值(首字符地址),+3得到之后的ST
	printf("%s\n", cpp[-1][-1] + 1);//EW
	//cpp指向c+1的地址,cpp[-1]等价于*(cpp - 1).-1指向c+2的地址,解引用得到c+2
	// 这里要注意的是*(cpp - 1)中,cpp-1是一个表达式,cpp的值不会因此改变,这很重要
	//(c+2)[-1]等价于*((c+2) - 1),(c+2) -1得到c+1,对c+1解引用得到c+1的值(首元素地址),+1得到之后的EW
	return 0;
}

        写到这里,指针系列就结束了。如果你有不懂的地方,可以留言,我会第一时间解答。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值