C[指针]

本文详细介绍了C语言中的指针概念,包括指针变量、野指针、空指针、万能指针以及指针与常量、多级指针、数组、函数、字符串的交互使用。还探讨了str系列的字符串处理函数。内容覆盖了指针的基本操作和常见应用,适合C语言学习者参考。
摘要由CSDN通过智能技术生成
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 = &num;
	short *p2 = (short *)&num; //int *强制类型转换为short *
	char *p3 = (char *)&num;
	
	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;
}

注:

  1. (二级指针) == (一级指针地址)
  2. *(二级指针) == (一级指针变量所存储地址)
  3. **(二级指针) == (一级指针所指向内容)

七.指针与数组

#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 &num; 
	//不要返回局部变量的地址
	//会造成悬空指针(指向已回收的内存空间)
}

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;
}

扩展:

  1. atoi将字符串转换为int类型的数据
  2. atof将字符串转换为float类型的数据
  • int/float n = atoi/atof (需要转换的字符串)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cforikl_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值