C语言指针入门(解决你的各种概念问题的总结,包括空指针、野指针、数组指针、指针数组、函数指针、指针函数、多级指针等等)

本文记录了一些C指针的定义,包括空指针、野指针、数组指针、指针数组、函数指针、指针函数、多级指针等等一些知识点的小结。
参考工程(vs19编写):代码下载

1.空指针:

没有赋值的指针变量(没有指向内存变量的地址),对空指针操作会造成程序的Core dump(段错误)

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int* p_i = 0;

	printf("p_i = %p \r\n", p_i);
	
	*p_i = 2;// 试图对空指针进行赋值操作,程序会崩溃
	
	return 0;
}

2.野指针:

指向的内存被释放了的指针,但指针的值不会清零。对其操作不可预知。

以下操作均为错误操作。

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int *p_i = (int *) malloc(sizeof(int));
	*p_i = 10;
	printf("p_i = %d \r\n",*p_i);

	free(p_i);
		
	*p_i = 2;
	printf("pi = %d \r\n",*p_i);

	return 0;
}
#include <stdio.h>
#include <stdlib.h>
int main()
{
	int *p_i = (int *) malloc(sizeof(int));
	*p_i = 10;
	printf("p_i = %d \r\n",*p_i);

	free(p_i);
		
	*p_i = 2;
	printf("pi = %d \r\n",*p_i);

	FILE *fp=fopen("1.txt","w");
	printf("fp is %p\r\n",fp);

	fclose(fp);

	printf("fp is %p\r\n",fp);
	//fclose(fp);
	return 0;
}

free ( p );带上p = 0;避免野指针错误。

#include <stdio.h>
#include <stdlib.h>
int main()
{
	char* p = (char*)malloc(sizeof(char)*4);
	
	free(p);
	p = 0;

	strcpy(p,"123");
	printf("%s\r\n", p);
	return 0;
}

3.数组

数组名、数组地址、数字首元素地址均为同一地址。

#include <stdio.h>
#include <stdlib.h>
int main()
{
	char num[20] = "hello word";
	
	printf("数组名     %p \r\n", num);
	printf("数组地址   %p \r\n", &num);
	printf("数组首元素 %p \r\n", &num[0]);

	printf("打印以数组名     %s \r\n", num);
	printf("打印以数组地址   %s \r\n", &num);
	printf("打印以数组首元素 %s \r\n", &num[0]);
	return 0;
}

结果:
在这里插入图片描述

sizeof的坑

sizeof(数组)会得到数组长度,但是通过函数传入数组的指针sizeof却是指针的大小。

如果数组的长度小于指针的大小,会导致程序崩溃,如下代码。不同系统指针大小不一致。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void fun(char* p);

int main()
{
	char str[3] ;

	printf("%d\r\n", sizeof(str));

	fun(str);
	
	
	return 0;
}

void fun(char* p)
{
	printf("%d\r\n", sizeof(p));
	memset(p, 0, sizeof(p));

	scanf("%s",p);

	printf("%%p :%p \r\n",p);
	printf("%%s :%s \r\n",p);

}

在这里插入图片描述

建议:不要在函数里面调用memset时,使用sizeof计算长度。

补充:结构体用sizeof计算指针也会出错。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Student
{
	int age;
	char name[21];
};

void fun(struct Student* p)
{
	printf("in fun() sizeof: %ld\r\n", sizeof(p));// sizeof(struct Student)
	memset(p, 0, sizeof(p));// 不建议使用,指清除了指针大小个字节
	// 建议改为下述代码,使用sizeof(struct Student)
    // memset(p, 0, sizeof(struct Student));
}

int main()
{
	struct Student stu = { 22,"LiHua" };

	printf("in main() sizeof :%ld\r\n", sizeof(stu));

	fun(&stu);
	return 0;
}

vs19编译结果:(指针4字节)

在这里插入图片描述

虚拟机结果:(指针8字节)

在这里插入图片描述

为什么结构体大小28?

因为int 4字节,结构体4字节对齐,所以为28。

4.地址的运算

地址可加减运算:+1表示加一个存储单位的地址,-1减一个存储单位的地址。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
	char    cc[4];
	int     ii[4];
	double  dd[4];

	printf("  char: %p %p %p %p\r\n", cc, cc+1, cc+2, cc+3);
	printf("   int: %p %p %p %p\r\n", ii, ii+1, ii+2, ii+3);
	printf("double: %p %p %p %p\r\n", dd, dd+1, dd+2, dd+3);
	return 0;
}

vs19运行结果:(char + 1,int + 4,double + 8)

在这里插入图片描述

同样的使用地址操作可以直接访问数组元素,如*(cc+1)cc[1]等效。

众所周知,C语言的标准库没有字符串截取函数。为什么?笔者认为可能是没有必要。因为可以直接地址运算操作地址实现。

字符串截取举例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
	char src[12] = "hello world";
	char dest[10];
	memset(dest, 0, 10);

	// 将src截取后面world赋值给dest
	strncpy(dest,src + 6, 5);// 地址操作

	printf("dest:%s\r\n",dest);
	return 0;
}

vs19运行结果:
在这里插入图片描述

5.字符串常量的地址:

在C里面字符串实际就是以‘\0’为结束符的char类型一维数组,故同样可以作为一个地址。它的值为字符串的首地址。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define HD "hello world"

int main()
{
	char *str = "hello world";
	
	char* str1 = "23333";

	printf("HD:%s \r\n", str + 6);

	printf("str1:%s\r\n",str1);
	return 0;
}

结果:
在这里插入图片描述

但因为是常量,所有并不能直接通过str[0] = ‘H’来改变字符串的值,使字符串变成“Hello word”。这种做法是错误的。

6.函数指针

定义:C程序每一个函数都有一个入口地址,所谓函数指针就是指向函数入口地址的指针变量。

应用:调用函数和做函数的参数。

定义格式:

返回值类型 (* 函数指针)(参数列表...)

返回值和参数列表应该和要指向的函数保持一致。

此外:函数名也是指针,即fun_max&fun_max等效。

改变指针变量的指向不同的函数,可以实现类似多态的效果。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 返回最大值
int fun_max(int x, int y)
{
	if (x > y) return x;

	return y;
}

// 返回最小值
int fun_min(int x, int y)
{
	if (x < y) return x;

	return y;
}

int main()
{
	int a = 2;
	int b = 5;
	// 定义函数指针
	int (*fun_ptr)(int, int);// 格式要匹配,返回值、参数及参数个数顺序都要一致。

	fun_ptr = &fun_max;		// 函数名也是指针,与fun_ptr = fun_max;等效
	printf("max:%d\r\n", fun_ptr(a,b));

	fun_ptr = fun_min;
	printf("min:%d\r\n", fun_ptr(a, b));

	return 0;
}

运行结果:

max:5
min:2 

函数指针常常和typedef组合使用,更直观。
如上可以写为:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 格式要匹配,返回值、参数及参数个数顺序都要一致。
typedef int (* _fun_ptr_t)(int,int);

// 返回最大值
int fun_max(int x, int y)
{
	if (x > y) return x;

	return y;
}
// 返回最小值
int fun_min(int x, int y)
{
	if (x < y) return x;

	return y;
}
int main()
{
	int a = 2;
	int b = 5;
	// 使用typedef定义函数指针
	_fun_ptr_t fun_ptr;
	
	fun_ptr = &fun_max;		// 函数名也是指针,与fun_ptr = fun_max;等效

	printf("max:%d\r\n", fun_ptr(a,b));

	fun_ptr = fun_min;
	printf("min:%d\r\n", fun_ptr(a, b));

	return 0;
}

7.指针函数:

定义:返回值为一个地址的函数

#include<stdio.h>

char* getword(char);

char* getword(char c)
{
    switch (c)
    {
    case'A':return"Apple";
    case'B':return"Banana";
    case'C':return"Cat";
    case'D':return"Dog";
    default:return"None";
    }
}
int main()
{
    char input;
    printf("请输入一个大写字母:\n");
    scanf("%c", &input);

    printf("%s\n", getword(input));
}

测试输入字母输出对应字符串

比如:

请输入一个大写字母:
C
Cat

8.数组指针与指针数组

指针数组:

int *p1[10];

解释:[]的优先级比*高,p[10]表示一个大小为10个单位的数组,int *再来修饰数组的内容,表示p1数组为一个存储10个int型指针的数组。

在这里插入图片描述

数组指针:指向一个数组的指针。

int (*p2)[10];

解释:(*p2)表示一个指针,[10]说明是一个数组,int修饰数组元素的类型,综上就是定义了一个指针p2,它指向有10个int元素数组的首地址。

无聊。。。。

在这里插入图片描述

9.多级指针

无聊的一个概念,说白了就是指针指向指针。

举例代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
	int         i = 2;
	int*       pi = &i;
	int**     ppi = &pi;
	int***   pppi = &ppi;
	printf("%d\r\n",       i);
	printf("%d\r\n",     *pi);
	printf("%d\r\n",   **ppi);
	printf("%d\r\n", ***pppi);

	return 0;
}

类似俄罗斯套娃,要是乐意可以一直“取地址”再“解引用”。

运行结果:(说明了这都是2)

2
2
2
2

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值