一、指针(2)
1.0无数据类型指针:void *
a)无数据类型指针(void *)概念:它也是一个指针变量,也保存一个地址,同样占4字节内存空间,只是它指向的内存区域的数据类型是不可知的,称这种指针为无数据类型指针
写法:void *
例如:void *p = &a;
b)无类型指针特点:
1.通过此种指针变量是无法获知指向的内存区域保存的数据的数据类型
2.不能直接对无类型指针进行解引用操作"*",因为你不知道指向的内存区域的数据类型,也就不知道将来要取几个字节的数据,如果要想通过无类型指针获取内存的数据,必须做数据类型的转换(为了代码的可读性建议采用强制转换)
3.无类型指针void *加减几,实际的地址就是加减几
void *p = 0;
p++; //p=1
p++; //p=2
4.所有情况的案例代码
例如:
int a = 0x12345678;
void *p = &a; //虽然p指向a,但是通过p无法获取a变量的数据类型
*p = 300; //gcc编译报错,不清楚到底取几个自己的数据,没办法只能给你报错
问:如何通过一个指针来获取a的数据呢?
答:方式1:
int a = 0x12345678;
int *p = &a;
printf("%#x\n", *p);
虽然能够一次性获取4字节数据,但是现在不想一次性获取4个字节数据,想获取其中1个字节数据或者2字节数据或者4字节数据,显然这种方式做不到,要想实现这个功能,只需做以下方式的代码:
方式2:通过有类型的指针变量来获取1字节,2字节
获取1字节:
int a = 0x12345678;
//char *p = &a; //隐式转换,代码可读性不高,gcc还要给个警告
char *p = (char *)&a; //强制转换,提高代码的可读性,将&a的int类型转换成char*
printf("%#x\n", *p++); //0x78
printf("%#x\n", *p++); //0x56
printf("%#x\n", *p++); //0x34
printf("%#x\n", *p++); //0x12
获取2字节:
int a = 0x12345678;
//short *p = &a; //隐式转换,代码可读性不高 gcc还要给个警告
short *p = ( short *)&a; //强制转换,将&a的int类型转换成short*
printf("%#x\n", *p++); //0x5678
printf("%#x\n", *p++); //0x1234
结论:此方法虽然满足要求,但是如果做隐式转换,gcc老给一个警告
问:能否将警告去掉呢?还有能够去掉指针变量++时跟类型相关问题呢?
答:通过无类型指针void *实现
方法3:通过无类型指针void *来获取1字节,2字节,4字节数据
获取1字节:
int a = 0x12345678;
void *p = &a; //定义一个无类型指针指向a,无需强转,关键是gcc不给警告
char *p1 = (char *)p; //将无类型指针强转为char*
printf("%#x\n", *p1++); //0x78
printf("%#x\n", *p1++); //0x56
printf("%#x\n", *p1++); //0x34
printf("%#x\n", *p1++); //0x12
或者:
printf("%#x\n", *(char *)(p+0)); //0x78,先将p强转为char *指针,然后取值
printf("%#x\n", *(char *)(p+1)); //0x56
printf("%#x\n", *(char *)(p+2)); //0x34
printf("%#x\n", *(char *)(p+3)); //0x12
获取1字节:
int a = 0x12345678;
void *p = &a; //定义一个无类型指针指向a,无需强转,关键是gcc不给警告
short *p1 = (short *)p; //将无类型指针强转为char*
printf("%#x\n", *p1++); //0x5678
printf("%#x\n", *p1++); //0x1234
或者:
printf("%#x\n", *(short *)(p+0)); //0x5678
printf("%#x\n", *(short *)(p+2)); //0x1234
1.1.指针和函数
a)指针作为函数的形参
结果:函数通过指针能够访问操作指向的内存
参考代码:例如:
void A(int *px, int *py, int *pm, int *pn)
{ //读查看
printf("%d %d %d %d\n", *px, *py, *pm, *pn);
//写修改
*px = ....
*py = ...
*pm = ...
*pn = ...
}
b)指针作为函数的返回值
结果:此类函数又称指针函数,也就是函数的返回值是一个指针,将来函数返回的是一个地址,跟返回的变量的内存生命周期相关,跟变量的使用范围无关
切记:千万要注意别返回一个野指针
语法格式:返回值数据类型 *函数名(形参表){函数体语句}
例如:
//定义指针函数
int g_a = 250; //定义全局非静态变量
static int g_b = 520; //定义全局静态变量
int *A(void)
{
int a = 200; //定义局部非静态变量
static int b = 100; //定义局部静态变量
return &a; //返回一个野指针,因为变量a的内存随着函数的返回而消失
//或者return &b; //可以,因为此内存一次分配终身使用
//或者return &g_a; //可以,因为此内存一次分配终身使用
//或者return &g_b; //可以,因为此内存一次分配终身使用
}
int main(void)
{
int *p = A();
*p = 30000; //修改变量的值
printf("%d\n", *p); //查看指向的内存的数据
return 0;
}
c)函数,指针,数组
函数访问数组的公式:
void A(int a[], int len); //其中a是数组的地址
等价于
void A(int *p, int len);
二、C语言字符串相关内容
2.1.回顾字符常量:用单引号包含,例如:'A','B','1'等
实际内存存储的是对应的整型数,ASCII码
占位符:%c
2.2.字符串定义:由一组连续的字符组成,并且用""包含起来,并且最后一个字符必须是'\0'
此'\0'表示字符串的结束,此'\0'的ASCII码是0
'0'的ASCII为48
注意:研究字符串最终研究的就是里面的一个一个的字符
例如:"abcefg\0"
2.3.字符串特点
a)字符串的占位符:%s
printf("%s\n", "abc\0"); //直接跟字符串
或者
printf("%s\n", 字符串的首地址);
b)字符串占用的内存空间是连续的,并且每个字节存储一个字符
注意:'\0'字符串的结束符如果后面还有内容,那么这些内容为无效的
例如:"abc\0efg\0"
printf("%s\n", "abc\0efg"); //abc
c)多个并列的字符串将来会由gcc帮你合并成一个字符串
"abc""efg"合并成"abcefg"
printf("abc""efg\n"); //abcefg
2.4.字符串和指针的那点事儿
a)定义一个字符指针变量并且指向一个字符串,本质是指向这个字符串的首地址,也就是指向字符串中第0个字符的首地址(也就是字符a的首地址)
定义并且初始化字符串指针变量形式:
char *p = "abcefg";
b)如果让一个字符指针变量指向一个字符串, 此字符串无需跟'\0',gcc将来帮你添加'\0'
d)切记:不能通过字符指针变量来修改字符串的值,只能查看
//读查看
printf("%s\n", p);
*(p + 2) = 'C'; //目标将第2个字符c变成C,做不到,报错
2.5.字符串和数组,两种写法:
a)写法1:
char a[] = {'a', 'b', 'c', '\0'};
注意:如果想把a当成字符串,需要手动最后添加'\0',(既是数组,又是字符串)
如果不添加a仅仅就是一个包含三个元素的数组,而不是有效字符串
b)写法2:
char a[] = "abc";
注意:无需添加'\0',gcc编译器将来帮你追加一个'\0'
c)切记:不管是哪种写法,字符数组中的元素是可以修改的
例如:将第2元素'c'修改为'C':
a[2] = 'C';
或者
*(a + 2) = 'C';
2.6.编写一个字符串比较函数my_strcmp
2.7.标准C语言提供的字符串操作函数
如果要用以下写好的字符串操作函数,需要添加头文件:#include <string.h>
a)strlen函数:功能获取字符串有效长度(不包括'\0')
例如:printf("长度:%d\n", strlen("abc")); //3
或者
char *p = "abc";
printf("长度:%d\n", strlen(p)); //3
b)strcat函数:功能是字符串拼接
例如:
char a[10] = "abc";
char *p = NULL;
p = strcat(a, "xyz"); //把xyz拼接在abc的后面保存到数组中
printf("%s %s\n", p, a); //abcxyz abcxyz
c)strcmp函数:功能是字符比较函数
例如:
char *p1 = "abc";
char *p2 = "xyz";
int ret = strcmp(p1, p2);
int ret = strcmp("abc", "xyz"); //本质最终传递的是字符串的首地址
d)strcpy:字符串拷贝函数,会覆盖原先的字符串
例如:
char a[10] = "abc";
char *p = NULL;
p = strcpy(a, "xyzmn");
printf("%s %s\n", p, a);
e)sprintf :格式化输出函数,功能把数字(250)转成字符串"250"保存到数组中
例如:
char a[50] = {0};
sprintf(a, "%d %g, %c", 250, 250.2, 'A');
printf("%s", a);