概述
内存与地址
指针=地址=编号
启动一个程序,系统在内存上给程序分配一块内存空间
32位,4G 一个一个字节组成的,每个字节都会有地址编号
地址,也就是内存的编号,我们把这个编号(地址)也叫做指针
指针变量
存放指针(地址)的变量
因为地址编号(0x0000 0000)如此,所以哦我们的指针变量占4个字节就可以存下
64位编译器,内存的编号范围是0x0000000000000000 - oxffffffffffffffff ,这样的编号,需要8个字节存下,所以指针变量也需要8个字节
指针变量的定义和初始化
int main()
{
int a = 10;
// 定义指针三步骤
// 1. *与符号结合代表室一个指针变量
// 2. 要保存谁的地址,将他的定义形势放在此处
// 3. 用*替换掉定义的变量
int *p = &a;
// 分析
// 1. 与*结合代表这是一个指针变量
// 2. p是变量,p的类型是将变量p本身拖黑,剩下的类型就是指针变量的类型,int *
// 3. 指针变量p用来保存什么类型数据的地址,将指针变量p和指针变量p最近的*拖黑
// 剩下的什么类型就保存什么类型数据的地址
// p = &a;
}
指针基础知识
指针变量保存谁的地址就指向了谁
// 在使用时,*与p结合,取p指针所指向那块空间的内容
*p = 100;
printf("%d", a);
在使用时,对一个表达式取*,就会对表达式减一级*,如果对表达式取&,就会加一级*
int *p;
int **q;
// int ** int *,&加一级*,所以两边相等
q = &p;
//
*q = p;
指针变量的大小
不管什么类型的指针,大小只和系统编译器有关,因为其存放的都是地址,地址大小在一个编译器中是相同的
int main()
{
char *p1;
short *p2;
int *p3;
int **p4;//p4也是一个指针变量 int **
printf("%d\n",sizeof(p1));
printf("%d\n", sizeof(p2));
printf("%d\n", sizeof(p3));
printf("%d\n", sizeof(p4));
system("pause");
return 0;
}
不同类型的指针变量,取指针指向的内容的宽度
指针的宽度=sizeof(将指针变量与指针变量最忌你的 拖黑,剩下的类型)*
宽度也叫步长
步长:指针加1跨过多少个字节
// char *p 1
// short *p 2
// int *p 4
// int **p sizeof(int *) 4
int main()
{
// 虽然指针都是存放的地址,但是不同类型的指针从地址中取出的数据多少不一样
int num = 0x01020304;
char *p1 = (char *)#//int *
short *p2 = (short *)#
int *p3 = #
//通过*取指针变量所指向那块空间内容时,取的内存的宽度和指针变量本身的类型有关
printf("%x\n",*p1); // 04 (04是地位)
printf("%x\n", *p2); // 0403
printf("%x\n", *p3); // 1020304
// 因为这三个变量中保存的都是同一个地址,所以打印出来相同
printf("%x\n",p1);
printf("%x\n",p2);
printf("%x\n",p3);
// +1求其步长,也就是sizeof(将指针变量与指针变量最忌你的* 拖黑,剩下的类型)
printf("%x\n",p1+1);
printf("%x\n",p2+1);
printf("%x\n",p3+1);
system("pause");
return 0;
}
野指针与空指针
指针变量也是变量,是变量就可以任意赋值,不要越界即可(32 位为 4 字节,
64 位为 8 字节),但是,任意数值赋值给指针变量没有意义,因为这样的指针
就成了野指针,此指针指向的区域是未知(操作系统不允许操作此指针指向的
内存区域)。所以,野指针不会直接引发错误,操作野指针指向的内存区域才
会出问题。
空指针
如果使用完指针,将指针赋值为NULL,使用时判断一下指针是否为NULL,就知道指针是否被使用
int *p = NULL;
*p = 200; // 因为p保存了0x0000 0000,这个地址是不可以使用的
printf("%d", *p);
万能指针
int a = 10;
void *p = &a;
//printf("%d\n", *p); err:表达式必须是指向完整对象类型的指针
// 万能指针使用时必须强转成想要的类型,指针转指针,将void*转为int*,然后再去*取地址,得到地址内的数据
printf("%d\n", *(int *)p);
const修饰的指针变量
int a = 10;
int b = 20;
// const修饰的是*还是p,取决于const的位置
// 不能通过*p改变指向空间的内容
const int *p = &a;
p = &b;
printf("%d\n", *p);
// const修饰的变量p
// p保存的地址不可以修改
int * const p2 = &a;
*p2 = b;
printf("a=%d\n",a);
多级指针
定义多级指针保存数据的地址时,定义的指针的类型只需要比保持的数据的类型多一级
int a = 10;
// *p int a int *p
int *p = &a;
// *q int *p int *(*q)
int **q = &p;
// 如果*和&相遇,则抵消
// **q == *(*q) == *(p) == a
// **q == *(*q) == *(&a) == a
// *符号结合,代表这个k是一个指针变量
// k是一个变量,k的类型,将变量k拖黑,剩下的类型
// k用来保存谁的地址,将变量k和最近的*一起拖黑,剩下的类型
int *********g;
int **********f = &g;
指针数组
指针结合数组
指针加1,扩过一个步长
int *p;
步长 = sizeof(int)
想要的到内存的数据,就该先得到数据的地址
// *(地址)得到的是地址里的内容
//int a[10] = {1,2,3,4,5,6,7,8,9,10};
int a[10] = { 0 };
//a 数组名,首元素的地址
int *p = a;//指针p保存的是首元素的地址
for (int i=0;i<sizeof(a)/sizeof(a[0]);i++)
{
//printf("%d ",a[i]);
//printf("%d ", *(p+i));
*(p + i) = i;
}
for (int i = 0; i<sizeof(a) / sizeof(a[0]); i++)
{
printf("%d ",a[i]);
}
system("pause");
return 0;
指针运算
两指针(类型一致)相减,得到的是中间夸多多少个元素(如果一个字符串中间有\0,strlen测不到,使用指针相减可以)
两指针相加,没有意义,报错
int a[10] = {1,2,3,4,5,6,7,8,9,10};
//sizeof(int [10])
int *p = a;
// 得到最后一个元素地址的方式,
// 因为(&a+1)跨过一整个数组,所以将其转为int *,再减1,就会得到最后一个元素的地址
//int *q = (int *)(&a + 1) - 1;
int *q = &a[9];
printf("%d\n",q-p);// p+9 == q
printf("%d\n",*(p+3));
//两指针相加没有意义
// printf("%d\n", p+q);err
system("pause");
return 0;
[]不是数组的专属
[] == *()
int main()
{
//[]是不是数组的专属
//[] == *()
int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
int *p = a;
for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
{
// 这些方式都可以
//printf("%d ",a[i]);//a[i] == *(a+i)
//printf("%d ", *(p+i));
//printf("%d ", p[i]);// p[i] == *(p+i)
printf("%d ", *(a + i));
}
/int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
// [] == *()
//p[0] == *(p+0)
int a = 10;
int *p = &a;
p[0] = 100;
// 会造成内存污染
//p[1] = 200;
printf("a=%d\n",a);
system("pause");
return 0;
}
指针数组
整形数组是一个数组,数组的每一个元素是整形
指针数组是一个数组,数组的每一个元素都是指针
通过二级指针操作指针数组
int a = 10;
int b = 20;
int c = 30;
// int *p1 = &a int *p2 = &b int *p3 = &c;
int *num[3] = {&a, &b, &c};
printf("%d\n", sizeof(num));
// &a == num[0]
for(int i = 0; i < sizeof(num) / sizeof(num[0]); i++) {
printf("%d\n",*num[i]);
}
// 定义一个指针用来保存数组num首元素的地址
// num = &num[0] = &(int *) = int **
// 这种解释好理解:num[0]是int *类型。要保存int *类型的地址,需要比它多一级*
int **k = num;
for(int i = 0; i < sizeof(num) / sizeof(num[0]); i++) {
printf("%d ", **(k+i));
}
指针作为函数的形参
指针作为函数的形参,可以改变实参的值
void swap(int *x, int *y) {
int k = *x;
*x = *y;
*y = k;
}
int main()
{
int a = 10;
int b = 20;
swap2(&a, &b);
printf("a=%d b=%d\n", a, b);
system("pause");
return 0;
}
数组作为函数的形参
数组作为函数的形参会退化为指针
void print_arr(int *b,int len)
{
int n = sizeof(b) / sizeof(b[0]); // *(b+0) == *b
printf("n=%d\n",n);
for (int i = 0; i <len; i++)
{
printf("%d ",b[i]);
}
printf("\n");
}
int main()
{
int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
print_arr(a,sizeof(a)/sizeof(a[0]));//打印数值的内容// &a[0] int *
system("pause");
return 0;
}
指针作为函数的返回值
int num = 0;//在函数外面定义的变量叫全局变量,整个工程都可以使用
//整个变量程序启动开辟空间,直到程序结束释放空间
int * getnum()
{
//{}中定义的变量叫局部变量,局部变量在函数结束之后的空间会被释放
srand(time(NULL));
// 这样是不对的,因为局部变量会被释放,所以不能返回局部变量的地址
// int num = read();
num = rand();
return #//
}
int main()
{
int * p = getnum();
printf("%d\n",*p);
system("pause");
return 0;
}
指针和字符串
指针与字符串
//指针与字符串
char a[] = "helloworld";//定义了一个字符数组,字符数组内容为helloworld\0
//定义一个指针用来保存数组首元素的地址
char * p = a;
printf("%s\n",p);//%s打印一个字符串,要的是首个字符的地址
printf("%s\n", p + 2);
printf("%c\n",*(p+3));
*p = 'm';
printf("%s\n", p);
//a++
p++;
*p = 'o';
printf("%s\n", p);
//printf("%d\n",sizeof(a));//11
//printf("%d\n", sizeof(p));//4 p是指针变量,大小为4
//printf("%d\n", strlen(a));//计算字符串的长度,从开始地址到\0结束的大小
//printf("%d\n", strlen(p));//计算字符串的长度,从开始地址到\0结束的大小
字符串常量
char a[] = "helloworld"; // 定义了一个字符数组,字符数组内容为helloworld
// 定义一个指针用来保存数组首元素的
char *p = a;
p = "abcdef" // 字符串常量存在文字常量区,""在使用时,取的是字符串首元素地址
// 文字常量区的内容是不可以改变的
printf("%s\n", p);
printf("%d\n",sizeof(p));//4
printf("%d\n", sizeof("abcdef"));//7
printf("%d\n", strlen(p));//6
printf("%d\n", strlen("abcdef"));//6
*p = 'm';
printf("%s\n",p)
字符指针作为形参
char * my_strcat(char * src, char *dst)
{
int n = strlen(src);
int i = 0;
while (*(dst + i) != 0)
{
*(src+n+i) = *(dst + i);
//src[n+i] = dst[i] ;
i++;
}
*(src + n + i) = 0;
return src;
}
int main()
{
char str1[128] = "hello";//hello123456\0
char str2[128] = "123456";
my_strcat(str1,str2);
printf("%s\n",my_strcat(str1, str2));
system("pause");
return 0;
}
const修饰的指针变量
//const修饰一个变量为只读
const int a = 10;
//a = 100; //err
//指针变量, 指针指向的内存, 2个不同概念
char buf[] = "aklgjdlsgjlkds";
//从左往右看,跳过类型,看修饰哪个字符
//如果是*, 说明指针指向的内存不能改变
//如果是指针变量,说明指针的指向不能改变,指针的值不能修改
const char *p = buf;
// 等价于上面 char const *p1 = buf;
//*p = '2'; //err 不能通过p修改那块空间的内容
p = "agdlsjaglkdsajgl"; //ok
char * const p2 = buf;
p2[1] = '3';
//p2 = "salkjgldsjaglk"; //err 不能修改k的指向
//p3为只读,指向不能变,指向的内存也不能变
const char * const p3 = buf;
字符指针数组*****
//字符指针数组
//是一个数组,每一个元素是字符指针
int main()
{
/*char *p1 = "heihei";
char *p2 = "haha";
char *p3 = "xixi";*/
//char *num[3] = { p1,p2,p3};
char *num[3]={ "heihei" ,"haha","xixi"};
//定义一个指针保存num数组首元素的地址 &num[0] num
char **p = num;
for (int i = 0; i < 3; i++)
{
// printf("%s\n",*(p+i));
printf("%s\n", p[i]);
}
printf("%c\n",*(*(p+1)+3));// *(p[1]+3) == p[1]3]
// p+1找到第二个字符指针数组的第二个元素,
// *(p+1)取地址内的内容0x2000
// *(p+1)+3获得第三个字母的地址
// *(*(p+1)+3)取第三个字母的地址里的内容,也就是得到那个字母
}
把这个搞懂,指针基本上就熟练了
字符指针数组作为main函数的形参
int main(int argc,char *argv[])
argc 是执行可执行程序是的参数个数
argv 是一个字符指针数组,保存的是参数(字符串)的首元素地址
//.*.exe hello 123456
//char *argv[] = { ".*.exe", "hello" "123456" };
int main(int argc,char *argv[])
{
/*printf("%d\n",argc);
printf("%s\n", argv[0]);
printf("%s\n", argv[1]);
printf("%s\n", argv[2]);
printf("%s\n", argv[3]);*/
for (int i = 0; i < argc; i++)
{
printf("%s\n", argv[i]);
}
}
字符串处理函数
char str1[128]
char str2[128]
-
strcpy(str1,str2);//将str2的字符拷贝至str1数组中,注意,str2遇到\0结束,会将\0拷贝至str1
-
strncpy(str1,str2,n);//将str2中的前n个字符拷贝至str1中,如果拷贝时不足n个,遇到\0拷贝结束
-
strcat(str1,str2)//将str2字符数组中的字符连接到str1字符数组中,遇到\0结束
-
strncat(str1,str2,n)//将str2字符数组中的n个字符连接到str1字符数组中,遇到\0结束
-
strncmp比较函数
char str1[] = "a\0bcdef";
char str2[] = "a\0cdrrr";
//str1数组中和str2数组拿出一个元素进行比较,相等继续往后比较
//比较的是字符的ascii值
//如果str1> str2 返回值等于1
//如果str1== str2 返回值等于0
//如果str1 <str2 返回值等于 - 1
//printf("%d\n",strcmp(str1,str2));
printf("%d\n", strncmp(str1, str2,3));
- sprintf: 组包函数
int len = sprintf(buf,“格式”,“数据”);
将数据安装格式组包,存放在数组buf中,sprintf函数的返回值是组完包的有效长度
int main()
{
int year = 2018;
int month = 10;
int day = 20;
char buf[1024] = "";
//printf("year=%d month=%d day=%d \n",year,month,day);
int len = sprintf(buf,"year=%d %cmonth=%d day=%d \n",year, 0,month, day);
//将数据安装格式组包,存放在数组buf中,sprintf函数的返回值是组完包的有效长度
//printf("%d\n",strlen(buf));
printf("len=%d\n",len);
printf("buf=[%s]",buf);
}
- sscanf 拆包函数
sscanf(buf,“格式”,数据);//将buf的内容格式化输出到数据
int main()
{
//%d 0-9的字符
int year =0 ;
int month = 0;
int day = 0;
char buf[1024] = "beijing:2018:t:10:20";
//scanf("%d:%d:%d",&year,&month,&day);//从键盘按照相应的格获取数据
sscanf(buf, "beijing:%d:t:%d:%d", &year, &month, &day);//从buf中按照`相应的格式获取数据
printf("%d %d %d\n",year,month,day);
}
- strchr(buf,ch)//在buf字符数组中查找字符ch出现的位置,如果成功返回此字符出现位置的地址,如果没有找到,返回NULL
- strstr(str1,str2)//在str1字符数组中查找str2字符串出现的位置,并且返回这个位置的地址
- strock,切割
char str[] = "15080015225&bangquan#82263&123456";
char *p[10] = {NULL};//初始化指针数组元素全部为NULL
//strtok
//char *p1 = strtok(str,"#");//在str1中将#切割,返回切割前面的字符串
//printf("%s\n",p1);
//char *p2 = strtok(NULL,"#");
//printf("%s\n",p2);
//char *p3 = strtok(NULL, "#");
//printf("%s\n", p3);
int i = 0;
do {
if(i == 0)
p[i] = strtok(str, "#&");
else
p[i] = strtok(NULL, "#&");
//i++;
} while ( p[i++] != NULL);//p[i] != NULL i=i+1 如果strtok的返回值等于NULL,代表切割完毕
i = 0;
while (p[i] != NULL)
{
printf("%s\n",p[i++]);
}
- atoi : 将字符串转整数
- atof: 将字符串转float类型的数据