2023-5-6首次编辑
Unit 7:指针
一. 指针变量
指针变量:
- 存放指针(地址)的变量
- 32位编译器,指针变量占4个字节
- 64位编译器,指针变量占8个字节
#define _CRT_SECIRE_NO_WARNINGS
#include <stdio.h>
//1.定义与初始化
void test1()
{
int a = 10;
int *p = &a; //指针变量保存谁的地址,就指向了谁
*p = 100; //取指针p所指向那块空间的内容
printf("%d\n", *p);
printf("%d\n", a);
}
//2.大小:
//无论任何类型的指针,大小只和系统编译器有关
void test2()
{
char *p1;
short *p2;
int *p3;
int **p4; //二级指针
printf("%d\n", sizeof(p1));
printf("%d\n", sizeof(p2));
printf("%d\n", sizeof(p3));
printf("%d\n", sizeof(p4));
}
//3.宽度:
//不同类型的指针变量,取指针指向的内容宽度
//(步长:指针+1跨过多少个字节)
void test3()
{
int num = 0x10203040;
int *p1 = #
short *p2 = (short *)# //int *强制类型转换为short *
char *p3 = (char *)#
printf("int:%x\n", *p1); //4个字节
printf("short:%x\n", *p2); //2个字节
printf("char:%x\n", *p3); //1个字节
}
int main()
{
test1();
test2();
test3();
system("pause");
return 0;
}
注意:
- 对表达式取*,就会对表达式减一级*
- 对表达式取&,就会对表达式加一级*
二.野指针
没有初始化的指针,指针的指向是随机的
注意:
- 指针保存的地址一定是定义过的(向系统申请过的)
#define _CRT_SECIRE_NO_WARNINGS
#include <stdio.h>
void test()
{
int *p; //野指针
*p = 200; //err:不可以操作野指针
printf("%d\n", *p);
}
int main()
{
test();
system("pause");
return 0;
}
三.空指针
值为NULL的指针
作用:
- 如果使用完指针后
- 将指针赋值为NULL
- 在使用时判断一下指针是否为NULL
- 就知道指针有没有被使用
#define _CRT_SECIRE_NO_WARNINGS
#include <stdio.h>
void test()
{
int a;
int *p = NULL; //空指针
*p = 200; //err:p保存了0x0000的地址,此地址不可以使用,非法
printf("%d\n", *p);
}
int main()
{
test();
system("pause");
return 0;
}
四.万能指针
可以保存任意地址的指针
#define _CRT_SECIRE_NO_WARNINGS
#include <stdio.h>
void test()
{
int a = 10;
short b = 10;
void *p = (void *)&a; //万能指针
void *q = (void *)&b;
//printf("%d\n", *p); //err:p是void *,不知道取几个字节的大小
printf("%d\n", *(int *)p); //*( (int *)地址)
//void c; //err
//不可以定义void类型的变量
//因为编译器不知道给变量分配多大的空间
}
int main()
{
test();
system("pause");
return 0;
}
五.指针与常量
#define _CRT_SECIRE_NO_WARNINGS
#include <stdio.h>
//1.常量指针:指向常量/变量的指针
void test1()
{
int a = 10;
int b = 20;
const int *p = &a;
//*p = 100; //err:指针所指空间的内容不可以更改
p = &b; //指针的指向可以更改
}
//2.指针常量:指针本身是常量
void test2()
{
int a = 10;
int b = 20;
int * const p = &a; //定义时要赋值
*p = 100; //指针所指空间的内容可以更改
//p = &b; //err:指针的指向不可以更改
}
//3.常量指针常量
void test3()
{
int a = 10;
int b = 20;
const int * const p = &a;
//*p = 100; //err:指针所指空间的内容不可以更改
//p = &b; //err:指针的指向不可以更改
}
int main()
{
test1();
test2();
test3();
system("pause");
return 0;
}
六.多级指针
#define _CRT_SECIRE_NO_WARNINGS
#include <stdio.h>
void test()
{
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
printf("%d\n", **q);
//*k int **q int ***k
int ***k = &q;
printf("%d\n", ***k);
}
int main()
{
test();
system("pause");
return 0;
}
注:
- (二级指针) == (一级指针地址)
- *(二级指针) == (一级指针变量所存储地址)
- **(二级指针) == (一级指针所指向内容)
七.指针与数组
#define _CRT_SECIRE_NO_WARNINGS
#include <stdio.h>
//1.数组指针:指向数组的指针
void test1()
{
int a[10] = { 0 }; //数组名 == 首元素地址
int *p = a; //指针p保存的是首元素地址
for (int i = 0; i < sizeof(a)/sizeof(a[0]); i++)
{
*(p + i) = i;
}
//打印
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]); //常用
}
printf("\n");
}
//2.指针数组:数组元素都是指针
void test2()
{
int a = 10;
int b = 20;
int c = 30;
int *num[] = { &a,&b,&c };
//大小
printf("数组指针大小=%d\n", sizeof(num));
//打印
for (int i = 0; i < sizeof(num)/sizeof(num[0]); i++)
{
printf("%d ", *num[i]); //num[0]是int *类型
}
printf("\n");
//保存数组num首元素地址
int **k = num;
for (int i = 0; i < sizeof(num)/sizeof(num[0]); i++)
{
//printf("%d ", **(k + i));
printf("%d ", *k[i]);
}
printf("\n");
}
int main()
{
//test1();
test2();
system("pause");
return 0;
}
八.指针与函数
#define _CRT_SECIRE_NO_WARNINGS
#include <stdio.h>
//1.指针作为函数的形参
//地址传递,可以改变实参的值
void swap(int *x, int *y)
{
int k = *x;
*x = *y;
*y = k;
printf("x=%d y=%d\n", *x, *y);
}
void test1()
{
int a = 10;
int b = 20;
swap(&a, &b);
printf("a=%d b=%d\n", a, b);
}
//2.数组作为函数的形参
//会退化为指针
//void print_arr(int b[100]) //int *b
void print_arr(int *b, int len)
{
int n = sizeof(b) / sizeof(b[0]); //b[0] == *(b+0) == *b
printf("%d\n", n);
for (int i = 0; i < len; i++)
{
printf("%d ", b[i]);
}
printf("\n");
}
void test2()
{
int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
print_arr(a, sizeof(a) / sizeof(a[0]));
}
//3.指针作为函数的返回值
//B.全局变量(整个工程都可以使用)
//程序启动时开辟空间,直到程序结束释放空间
int num = 0;
int *getnum()
{
srand(time(NULL));
//A.局部变量
//函数结束之后空间会被释放
//int num = rand();
return #
//不要返回局部变量的地址
//会造成悬空指针(指向已回收的内存空间)
}
void test3()
{
int *p = getnum();
printf("%d\n", *p);
}
int main()
{
//test1();
//test2();
test3();
system("pause");
return 0;
}
九.指针运算
相加:
- 无意义
相减:
- 两指针需要类型一致
- 结果为中间所跨过的元素个数
#define _CRT_SECIRE_NO_WARNINGS
#include <stdio.h>
void test()
{
int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
int *p = a;
int *q = &a[9];
printf("%d\n", q - p); //中间所跨过元素
printf("%d\n", *(p + 3));
//printf("%d\n", p + q); //err
}
int main()
{
test();
system("pause");
return 0;
}
十.指针与字符串
#define _CRT_SECIRE_NO_WARNINGS
#include <stdio.h>
//字符数组与字符指针
void test1()
{
char a[] = { "helloworld" }; //字符数组:helloworld\0
char *p = a; //保存数组首元素地址
//打印字符串
printf("%s\n", p);
printf("%s\n", p + 2);
//打印单个字符
printf("%c\n", *(p + 3));
*p = 'm';
printf("%s\n", p); //melloworld
p++;
*p = 'o';
printf("%s\n", p); //olloworld
//大小
printf("字符串大小=%d\n", sizeof(a));
printf("字符指针大小=%d\n", sizeof(p));
//个数(遇'\0'结束)
printf("字符串元素个数=%d\n", strlen(a));
printf("字符指针元素个数=%d\n", strlen(p));
}
//字符串常量
void test2()
{
char *p = "abcdef";
//字符串常量存在文字常量区
//""在使用时,取的是字符串首元素地址
//打印
printf("%s\n", p);
//大小
printf("%d\n", sizeof("abcdef"));
//个数
printf("%d\n", strlen("abcdef"));
//*p = 'm'; //err:文字常量区的内容不可以改变
//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;
}
void test3()
{
char str1[128] = "hello"; //hello\0
char str2[128] = "123456"; //123456\0
printf("%s\n", my_strcat(str1, str2)); //连接两个字符数组:hello123456\0
}
//const修饰字符指针
void test4()
{
char buf[] = "hello";
char str[] = "abc";
//A.常量字符指针
const char *p = buf;
//*p = 'b'; //err:不可以修改所指空间的内容
p = str; //可以修改指针指向
printf("%s\n", p);
//B.字符指针常量
char *const k = buf;
*k = 'b'; //可以修改所指空间的内容
//k = str; //err:不可以修改指向
printf("%s\n", k);
}
//字符指针数组
void test5()
{
char *num[3] = { "heihei","xixi","haha" };
char **p = num;
//打印字符指针数组
for (int i = 0; i < strlen(num); i++)
{
//printf("%s\n", num[i]);
printf("%s\n", p[i]);
}
//打印其中单独一个字符
printf("%c\n", *(*(p + 1) + 3)); //*(p[1]+3) == p[1][3]
}
//字符指针数组作为main函数的形参
int main(int argc, char *argv[])
{
//argc:可执行程序的个数
//argv:字符指针数组,保存的是参数(字符串)的首元素地址
printf("%d\n", argc);
for (int i = 0; i < argc; i++)
{
printf("%s\n", argv[i]);
}
//test1();
//test2();
//test3();
//test4();
//test5();
system("pause");
return 0;
}
十一.str系列
#include <string.h>
1.strcpy
char *strcpy( char *to, const char *from );
功能:复制字符串from 中的字符到字符串to ,包括空值结束符,返回值为指针to.
2.strncpy
char *strncpy( char *to, const char *from, size_t count );
功能:将字符串from 中至多count个字符复制到字符串to中。如果字符串from 的长度小于count,其余部分用'\0'填补。返回处理完成的字符串。
3.strcat
char *strcat( char *str1, const char *str2 );
功能:函数将字符串str2 连接到str1 的末端,并返回指针str1.
4.strncat
char *strncat( char *str1, const char *str2, size_t count );
功能:将字符串from 中至多count个字符连接到字符串to中,追加空值结束符,返回处理完成的字符串.
5.strcmp
int strcmp( const char *str1, const char *str2 );
功能:比较字符串str1 and str2 ,比较的是字符ASCII值,遇'\0'结束.
返回值:
str1 > str2 : 1;
str1 == str2 : 0;
str1 < str2 : -1.
6.strncmp
int strncmp( const char *str1, const char *str2, size_t count );
功能:比较字符串str1 和 str2中至多count个字符,比较的是字符ASCII值,遇'\0'结束.
返回值:
str1 > str2中count个字符 : 1;
str1 == str2中count个字符 : 0;
str1 < str2中count个字符 : -1.
7.strchr
char *strchr( const char *str, int ch );
功能:函数返回一个指向str 中ch 首次出现的位置,当没有在str 中找ch 到返回NULL.
8.strstr
char *strstr( const char *str1, const char *str2 );
功能:函数返回一个指针,它指向字符串str2 首次出现于字符串str1 中的位置,如果没有找到,返回NULL.
9.strtok
char *strtok( char *str1, const char *str2 );
功能:
函数返回字符串str1 中紧接“标记”的部分的指针,字符串str2 是作为标记的分隔符.如果分隔标记没有找到,函数返回NULL.为了将字符串转换成标记,第一次调用str1 指向作为标记的分隔符.之后所有的调用str1 都应为NULL.
#include <stdio.h>
1.sprintf
int sprintf( char *buffer, const char *format, ... );
功能:函数sprintf()和printf()类似,只是把输出发送到buffer(缓冲区)中.返回值的是写入字符数量.
2.sscanf
int sscanf( const char *buffer, const char *format, ... );
功能:函数sscanf()和scanf()类似, 只是输入从buffer(缓冲区)中读取.
#define _CRT_SECIRE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
//1.strcpy/strncpy
void test1()
{
char str1[128] = "12345";
char str2[128] = "helloworld";
//strcpy(str1, str2);
//printf("%s\n", str1); //helloworld
strncpy(str1, str2, 5);
printf("%s\n", str1); //hello
}
//2.strcat/strncat
void test2()
{
char str1[128] = "123456";
char str2[128] = "helloworld";
//strcat(str1, str2);
//printf("%s\n", str1); //123456helloworld
strncat(str1, str2, 5);
printf("%s\n", str1); //123456hello
}
//3.strcmp
void test3()
{
char str1[] = "a\0bcdef";
char str2[] = "a\0rrrrr";
printf("%d\n", strcmp(str1, str2)); //0
printf("%d\n", strncmp(str1, str2, 3)); //0
}
//4.strchr
void test4()
{
char str[] = "helloworld";
char *p = strchr(str, 'l');
printf("%s\n", p); //lloworld
}
//5.strstr
void test5()
{
char str1[] = "helloworld";
char str2[] = "wo";
char *p = strstr(str1, str2);
//先找到首字符,如果找到了,再往后依次比较
printf("%s\n", p); //world
}
//6.strtok
void test6()
{
char str[] = "hello&world#abc#123&678";
char *p[10] = { NULL }; //初始化指针数组元素全部为NULL
char *p1 = strtok(str, "#");
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, "#&");
}
} while (p[++i] != NULL);
i = 0;
while (p[i] != NULL)
{
printf("%s\n", p[i++]);
}
}
//7.sprintf组包函数
void test7()
{
int year = 2023;
int month = 2;
int day = 23;
char buf[1024] = "";
//printf输出到屏幕
//printf("year=%d,month=%d,day=%d\n", year, month, day);
//sprintf输出到数组buf
int len = sprintf(buf, "year=%d,month=%d,day=%d\n", year, month, day);
//strlen计算长度
//printf("%d\n", strlen(buf));
//sprintf返回组完包的有效长度
printf("%d\n", len);
printf("buf=[%s]\n", buf);
}
//8.sscanf拆包函数
void test8()
{
int year = 0;
int month = 0;
int day = 0;
char buf[1024] = "beijing:2023:2:23";
//scanf从键盘按照相应的格式获取数据
//scanf("%d:%d:%d", &year, &month, &day);
//sscanf从数组按照相应的格式获取数据
sscanf(buf, "beijing:%d:%d:%d", &year, &month, &day);
printf("%d %d %d\n", year, month, day);
}
int main()
{
//test1();
//test2();
//test3();
//test4();
//test5();
//test6();
//test7();
test8();
system("pause");
return 0;
}
扩展:
- atoi将字符串转换为int类型的数据
- atof将字符串转换为float类型的数据
- int/float n = atoi/atof (需要转换的字符串)