当前位置:   article > 正文

c语言 指针_c语言指针

c语言指针

读我

这篇文章我很少修改,但是指针却又很牛逼,如文章中有哪些理解的不到位的地方,请留下宝贵的意见

c语言 面试前必备基础知识

c语言 指针概念

指针是个变量,存放的是地址

#include <stdio.h>
int main()
{
	int a = 10; // 在内存中开辟一块空间,存储10
	int* p = &a; // 取变量a的地址,可以使用&操作符。
	//将a的地址存放在p变量中,故p就是一个存放地址的变量。
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

解引用

#include <stdio.h>
int main()
{
	int a = 10; // 在内存中开辟一块空间,存储10
	int* p = &a; // 取变量a的地址,可以使用&操作符。
	

	// 因为p里面存储的是a的地址,要输出a的值,故要解引用,即加*
	printf("%d\n",*p);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
c语言 指针大小

指针的大小在32位平台是4个字节,在64位平台是8个字节。

解释:

  1. 指针只是存储另一个变量的地址,而不是存储变量本身,那么就不关注变量本身是int型,还是char型的了。
  2. 1个字节8个比特。32位OS的指针大小位为(32/8)个字节,依次类推
c语言 指针类型的意义

我们知道 指针的大小在32位平台是4个字节,在64位平台是8个字节,那么指针的类型决定了指针向前或者向后走一步有多大(距离)。举例: int 类型的指针向前走一步是4个字节的距离,char 类型的指针向前走一步是1个字节的距离……

在这里插入图片描述

在这里插入图片描述

总结:指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。 比如char* 的指针解引用就只能访问一个字节,而int* 的指针的解引用就能访问四个字节。

c语言 int型指针
c语言 char型指针
#include <stdio.h>

int main()
{
	char c = 'A';
	int b = 7;
	// char型指针
	char* pc = &c;
	// int型指针
	int* pb = &b;
	// 对char型指针 解引用
	printf("%c\n",*pc); // A
	// int型指针 解引用 
	printf("%d\n",*pb); // 7

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
c语言 void型指针

void型指针,可以接收任何类型的地址,但是不可以运算和解引用。

#include <stdio.h>

int main() {
	int a = 10;
	int* p = &a;
	char* c = &a;
	void* v = &a;
	//v++; // error 不可以运算
	//*v = 88; // error 不可以解引用
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在这里插入图片描述

c语言 数组指针

整型指针--->指向整型地址

字符指针--->指向字符地址

数组指针--->存放数组地址

#include <stdio.h>

int main()
{
    int b[10] = { 0 };
    int(*pb)[10] = &b;
	/*
	1. &b表示数组首元素地址,那么要存放该数组的地址,就需要指针,所以要声明一个指针,即*pb,但怎么才能是数组指针呢?
	
	答:可以用(*pb)[],表示一个数组指针,那是什么类型的呢?
	答:是整形的 
	*/ 

	/*总结来说,数组指针存放数组地址,其实还是一个指针*/
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

使用案例

#include<stdio.h>

int main() {

    int a[3][4] = {{1, 2,  3,  4},
                   {5, 6,  7,  8},
                   {9, 10, 11, 12}};
	int (*p)[4] = a;
    int i, j;
    for (i = 0; i < 3; i++) {
        for (j = 0; j < 4; j++) {
            printf("%d ",*(*(p+i)+j)); // 解释说明如下
        }
        printf("\n");
    }

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

在这里插入图片描述
在这里插入图片描述

c语言 指针数组

整型数组--->存放整型

字符数组--->存放字符

指针数组--->存放指针

#include <stdio.h>
int main(){
    int a = 16, b = 932, c = 100;
    //定义一个指针数组
    int *arr[3] = {&a, &b, &c};// 指针数组大小为3,存放3个指针,每个指针指向一个地址

    printf("%d, %d, %d\n", *arr[0], *arr[1], *arr[2]);  // 指针里面存放的是地址,所以要加*,解引用地址
    return 0;
} // 但实际编程环境中,不会这样用指针数组。这里只是便于理解
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

使用案例

#include <stdio.h>

int main() {
    int a1[] = {1, 1, 1};
    int a2[] = {2, 2, 2};
    int a3[] = {3, 3, 3};
    int i, j;
    int *pa[] = {a1, a2, a3};
    for (i = 0; i < 3; i++) {
        for (j = 0; j < 3; j++)
            printf("%d ", *(pa[i] + j)); // pa[i]:分别找到3个数组首元素地址
        printf("\n");
    }
    return 0;
}

/*int* pa[]--->[]的优先级比*的优先级高,那么[]先和pa结合,成为一个数组。
然后这个数组要存放的数据类型是int*,即要存放的是int形指针。*/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

在这里插入图片描述

c语言 数组传参(结合指针)
实参(一维数组)—> 形参(指针)
#include <stdio.h>

void test2(int* arr, int len)
{
	// 编写代码逻辑
}

int main()
{
	int arr[3]={1,2,3};
	test2(arr, 3);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在这里插入图片描述

实参(二维数组)—> 形参(指针)
#include <stdio.h>

void test2(int (*arr)[], int r, int c)
{
	// 编写代码逻辑
}

int main()
{
	int arr[3][5] = { 1,2,3,4,5,6,7,8,9,10 };
	test2(arr, 3, 5);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在这里插入图片描述

实参(一级指针)—> 形参(指针)
#include <stdio.h>

void print(int* p, int sz)
{
	// 编写代码逻辑
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9 };
	int* p = arr;
	// 一级指针p
	print(p, 10);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

编写形参的技巧
在这里插入图片描述

实参(二级指针)—> 形参(指针)
# include<stdio.h>

void test(int** ptr)
{
	// 编写代码逻辑
}

int main()
{
	int n = 10;
	int* p = &n;
	int** pp = &p;
	// 二级指针pp
	test(pp);
	test(&p);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

在这里插入图片描述

实参(指针数组)—> 形参(指针)
#include <stdio.h>

void test(int **arr[]) {

}

int main()
{
	int* pa = NULL, * pb = NULL;
	int* arr[2] = { pa, pb };
	test(arr);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在这里插入图片描述

c语言 函数指针
c语言 函数指针 简介 简单运用

建议先了解数组指针,不然不好理解函数指针

数组指针:指向数组的地址。其实还是一个指针,只不过是指向了数组的地址。

函数指针:指向函数的地址。其实还是一个指针,只不过是指向了函数的地址。

#include <stdio.h>

void test(int x, int y)
{
	printf("%d\n", x + y);
}

int main()
{
	// &函数名 和 函数名 都是函数的地址
	void (*pfun1)(int, int)=test; 
	/*
	1. 函数名test是一个地址,那么接收这个函数地址时就需要用指针,所以声明一个指针*pfun1
	2. 函数指针 函数指针 这个指针就要指向一个函数,那么怎么才能表达它指向一个函数呢?
	答:可以用(*pfun1)(),表示一个指针指向函数,那么指针指向函数的参数是什么类型呢?
	答:指针指向函数的参数的类型为int int ,故可以写为(*pfun1)(int, int),那么指针指向函数的返回值是什么类型呢?
	答:void,故可以写为void (*pfun1)(int, int)
	*/

	/*
	1. pfun1表示一个指针,对指针解引用,即*pfun1,拿到test函数地址
	2. 拿到test函数地址后不就可以调用函数了嘛 test(3, 4)<=>(*pfun1)(3, 4)
	*/
	(*pfun1)(3, 4);

	//最后再看
	pfun1(3, 4); // 上边也可以写成这样的形式。为什么?没有那么多为什么,死记住。(这种写法往往还经常用)
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

在这里插入图片描述

c语言 用函数指针实现类似库函数qsort()的功能

用冒泡排序实现一个类似于库函数qsort()的功能

#include <stdio.h>
#include <stdlib.h> // qsort()
#include <string.h> // strcmp()

/*用冒泡排序实现一个类似于库函数qsort()的功能*/
struct Stu
{
	char name[20];
	int age;
};

/*
第一个参数:是待排序数组首元素地址
第二个参数:是待排序数组的个数
第三个参数:是待排序数组的每个元素的大小-单位是字节
第四个参数:是函数指针,指向自定义比较函数的地址-这个函数指针的两个参数是:代比较元素的地址
*/

void _swap(char* p1, char* p2, int width)
{
	int w = 0;
	//一个一个字节的进行交换
	for (w; w < width; w++)
	{
		char tmp = *p1;
		*p1 = *p2;
		*p2 = tmp;
		p1++;
		p2++;

	}
}


// 自定义年龄的比较规则
int cmp_stu_by_name(const void* e1, const void* e2)
{
	// strcmp():字符串比较函数
	return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}

void bubble_sort(void* base, int num, int width, int(*cmp)(void* e1, void* e2))
{
	int i = 0, j = 0, res = 0;
	// 比较的趟数
	for (i; i < num - 1; i++)
	{
		// 每一趟比较的对数
		for (j; j < num - 1 - i; j++)
		{
			/*
			1、(char*)base:表示先把void类型的base指针,先转换为char类型1个字节的指针
			2、width:表示字节数
			3、j*width:表示j个数组元素的宽度
			4、(char*)base + j * width:表示第j个元素的地址
			*/
			res = cmp((char*)base + j * width, (char*)base + (j + 1) * width);
			if (res > 0)
				_swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
		}
	}
}



int main() {
	int i = 0;
	struct Stu s[] = { {"zhangsan",10}, {"lisi", 30},{"wangwu",20} };
	int num = sizeof(s) / sizeof(s[0]);
	bubble_sort(s, num, sizeof(s[0]), cmp_stu_by_name);
	for (i; i < num; i++)
		printf("%s\n", s[i].name);
	return 0;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75

在这里插入图片描述

c语言 函数指针数组

必须先了解函数指针

#include <stdio.h>

/*以下四个函数分别为 加减 乘 除*/

int add(int a, int b)
{
	return a + b;
}

int sub(int a, int b)
{
	return a - b;
}

int mul(int a, int b)
{
	return a * b;
}

int div(int a, int b)
{
	return a / b;
}

int main()
{
	//分别将四个函数的地址,存放在一个函数指针数组*p中
	int(*p[4])(int x, int y) = {add, sub, mul, div };
	return 0;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

实际运用小栗子

#define _CRT_SECURE_NO_WARNINGS 1

/*实现一个简单的计算器*/

#include <stdio.h>

int add(int a, int b)
{
	return a + b;
}

int sub(int a, int b)
{
	return a - b;
}

int mul(int a, int b)
{
	return a * b;
}

int div(int a, int b)
{
	return a / b;
}

int main()
{
	int x, y;
	int input = 1;
	int ret = 0;
	// p是一个函数指针数组
	int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表
	while (input)
	{
		printf("*************************\n");
		printf(" 1:add 2:sub \n");
		printf(" 3:mul 4:div \n");
		printf("*************************\n");
		printf("请选择:");
		scanf("%d", &input);
		if ((input <= 4 && input >= 1))
		{
			printf("输入操作数 数字间用空格隔开:");
			scanf("%d %d", &x, &y);
			ret = (*p[input])(x, y);
		}
		else
			printf("输入有误\n");
		printf("ret = %d\n", ret);
	}
	return 0;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
c语言 指向函数指针数组的指针

必须先了解函数指针数组

#include <stdio.h>

void test(const char* str) {
	printf("%s\n", str);
}

int main() {
	// 函数指针pfun
	void (*pfun)(const char*) = test;     
	// 函数指针的数组pfunArr
	void (*pfunArr[5])(const char* str);
	pfunArr[0] = test;
	//指向函数指针数组pfunArr的指针ppfunArr
	void (*(*ppfunArr)[10])(const char*) = &pfunArr;
	return 0;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
c语言 回调函数

回调函数就是一个通过函数指针调用的函数。如果你把某函数的地址作为参数传递给另一个函数,当这个函数指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

#include <stdio.h>

void print(char* str)
{
	printf("%s\n", str);
}


void test(void (*pfun)(char*)) {
	pfun("hello world!"); // 触发print
}

int main() {
	test(print); // 虽然这里调用了print函数,但是print函数效应,并没有立即发生
	return 0;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

在这里插入图片描述

c语言 野指针

野指针成因:

1、指针未初始化

#include <stdio.h>

int main()
{
	int* p; // 局部变量指针 p 未初始化,默认指向一个随机地址
	*p = 20;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

可以看看指针是怎么初始化的

2、指针越界访问

#include <stdio.h>

int main()
{
	int arr[10] = { 0 };
	int* p = arr;
	int i = 0;
	for (i; i <= 11; i++)
	{
		// 当指针p指向的范围超出数组arr的范围时,p就是野指针
		*(p++) = i;
	}
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

3、指针指向的空间释放

c语言 指针的初始化
#include <stdio.h>

int main()
{
	char str1[] = "hello";
	// 初始化指针
	char* st = NULL;
	// 使用指针
	st = str1;
	// 指针解引用
	printf("%c",*st); // h
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
c语言 面试题小练习:1
#include <stdio.h>

int main()
{
	// 以下这句话不是把"hello"整个字符串赋值给了*p,而是把首字母的地址赋值给了*p
	char* p = "hello";
	// 打印第一个元素
	printf("%c\n", *p);
	// 打印第一个元素的地址
	printf("%p\n", p);
	// 找到第一个元素的首地址,然后打印出整个字符串
	printf("%s\n", p);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

在这里插入图片描述

除了字符串可以在找到首字符地址时,接连打印出其它字符,其它类型的不可以

在这里插入图片描述

c语言 面试题小练习:2
#include <stdio.h>

int main()
{
	char* p = "hello"; // "hello"是一个常量字符串
	*p = 'x'; // 错误代码
	// 找到第一个元素的首地址,然后打印出整个字符串
	printf("%s\n", p);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

因为 "hello" 是一个常量字符串,值不能被修改,所以 *p = 'x' 是错误代码。修改为最标准的形式为 const char* p = "hello";

c语言 面试题小练习:3
#include <stdio.h>
int main()
{
	char str1[] = "hello";
	char str2[] = "hello";
	char* str3 = "hello";
	char* str4 = "hello";
	if (str1 == str2)
		printf("str1 and str2 are same\n");
	else
		printf("str1 and str2 are not same\n");
	if (str3 == str4)
		printf("str3 and str4 are same\n");
	else
		printf("str3 and str4 are not same\n");
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

在这里插入图片描述

解释如下:

1、char* str3 = “hello”; 代表常量字符串,不可以被修改,那么就没有必要在内存中,存储2份,1份即可。

2、每声明一个数组,在内存中就会申请一个地址,绝不重复。

c语言 面试题小练习:4

在这里插入图片描述

#include<stdio.h>

int main()
{
	int a[] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
	int* p = a+5,* q = NULL;
	*q = *(p + 5);
	printf("%d %d", *p,*q);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在这里插入图片描述

c语言 面试题小练习:5
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

/*求字符串的长度*/
int test(char* s)
{
	char* p = s;
	while (*++p); // 先进行指针运算即+1,然后对指针解引用
	return p - s; // 指针 - 指针
}

/*计算字符串占用内存字节的个数*/
int test1(char* s)
{
	char* p = s;
	while (*p++); // 先对指针解引用,然后p++ 
	// 虽然*p=0 时,while()循环进不去了,但是指针还要在走一步
	return (p - s);
}

int main()
{
	char str[] = "hello world";
	printf("%d\n", test(str)); // 11
	printf("%d\n", test1(str)); // 12
	
	
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

在这里插入图片描述

在这里插入图片描述

c语言 指针精髓小题1
#include <stdio.h>

int main() {
	// 一维数组
	/*
	* 数组名表示首元素的地址,除了以下这2种情况
	* 1. sizeof(数组名):数组名表示整个数组
	* 2. &数组名:数组名表示整个数组
	*/
	// 以下为32位平台测试
	int a[] = { 1,2,3,4 };
	
	// 16 满足以上条件->sizeof(数组名):数组名表示整个数组
	printf("%d\n", sizeof(a));
	
	// 4 这里的数组名a表示首元素的地址,a+0还是首元素地址,地址的大小是4个字节
	printf("%d\n", sizeof(a + 0)); 
	
	// 4 这里的数组名a表示首元素的地址,*a表示首元素,首元素所占的空间为4个字节
	printf("%d\n", sizeof(*a)); 
	
	// 4 这里的数组名a表示首元素的地址,a+1表示第2个元素的地址,地址的大小是4个字节
	printf("%d\n", sizeof(a + 1)); 
	
	// 4 a[1]表示第2个元素,该元素所占的空间为4个字节
	printf("%d\n", sizeof(a[1])); 
	
	// 4 满足以上条件->&数组名:数组名表示整个数组。
	// 取出的是整个数组的地址,但是整个数组的地址也是地址,地址的大小是4个字节
	printf("%d\n", sizeof(&a)); 
	
	// 16 &a是数组的地址,数组的地址解引用访问的是整个数组
	printf("%d\n", sizeof(*&a));
	
	// 4 &a是数组的地址,&a+1虽然跳过了整个数组的地址,但依旧是地址,地址的大小是4个字节
	printf("%d\n", sizeof(&a + 1));
	
	// 4 &a[0]是第一个元素的地址,地址的大小是4个字节
	printf("%d\n", sizeof(&a[0]));
	
	// 4 &a[0] + 1是第二个元素的地址,地址的大小是4个字节
	printf("%d\n", sizeof(&a[0] + 1));
	
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

在这里插入图片描述

c语言 指针精髓小题2
#include <stdio.h>

int main() {
	// 字符数组
	/*
	* 数组名表示首元素的地址,除了以下这2种情况
	* 1. sizeof(数组名):数组名表示整个数组
	* 2. &数组名:数组名表示整个数组
	*/
	// 以下为32位平台测试
	char arr[] = { 'a','b','c','d','e','f' };
	
	// 6 满足以上条件->sizeof(数组名):数组名表示整个数组
	printf("%d\n", sizeof(arr));
	
	// 4 这里的arr,表示数组首元素的地址,arr+0还是首元素的地址,地址的大小是4个字节
	// 在这里你可能有疑问了?不是char型地址吗?不应该是1个字节吗,怎么会是4个字节?请看上面专题  c 语言 指针类型的意义 然后类比
	printf("%d\n", sizeof(arr + 0));
	
	// 1 arr是首元素的地址,*arr是首元素,首元素所占的空间是1个字节
	printf("%d\n", sizeof(*arr));
	
	// 1
	printf("%d\n", sizeof(arr[1]));
	
	// 4 满足以上条件->&数组名:数组名表示整个数组。但是数组地址也是个地址,地址的大小是4个字节
	printf("%d\n", sizeof(&arr));
	
	// 4 满足以上条件->&数组名:数组名表示整个数组。&arr + 1是跳过了整个数组后的地址,只要是地址,地址的大小就是4个字节
	printf("%d\n", sizeof(&arr + 1));
	
	// 4 &arr[0]表示第一元素的地址,&arr[0] + 1表示第二个元素的地址,地址的大小是4个字节
	printf("%d\n", sizeof(&arr[0] + 1));

	// 随机值 因为数组末尾没有结束标志\0,所以'\0'位置放了一个随机值。这里的arr表示首元素的地址
	printf("%d\n", strlen(arr));
	
	// 随机值 因为数组末尾没有结束标志\0,所以'\0'位置放了一个随机值。这里的arr表示首元素的地址,arr + 0还是首元素的地址
	printf("%d\n", strlen(arr + 0));
	
	// 报错 这里的arr表示首元素的地址,*arr表示解引用,找到了首元素'a','a'对应ascll码表十进制的值为97,
	// 那么strlen函数就把97当作起始地址值,开始从这里向后面数数。那么自然而然就会出现错误,即非法访问内存了
	//printf("%d\n", strlen(*arr));
	
	// 报错 理由同上
	//printf("%d\n", strlen(arr[1]));
	
	// 随机值 满足以上条件->& 数组名:数组名表示整个数组。因为数组末尾没有结束标志\0,所以'\0'位置放了一个随机值。
	printf("%d\n", strlen(&arr));
	
	// 随机值 满足以上条件->& 数组名:数组名表示整个数组。&arr + 1是跳过了整个数组后的地址。
	printf("%d\n", strlen(&arr + 1));
	
	// 随机值 &arr[0]表示第一元素的地址,&arr[0] + 1表示第二个元素的地址,因为数组末尾没有结束标志\0,所以'\0'位置放了一个随机值。
	printf("%d\n", strlen(&arr[0] + 1));
	
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

在这里插入图片描述

c语言 指针精髓小题3
#include <stdio.h>

int main() {
	/*
	* 数组名表示首元素的地址,除了以下这2种情况
	* 1. sizeof(数组名):数组名表示整个数组
	* 2. &数组名:数组名表示整个数组
	*/
	// 以下为32位平台测试
	char arr[] = "abcdef";
	
	// 7 满足以上条件->sizeof(数组名):数组名表示整个数组。因为字符串末尾隐藏了\0,所以整个数组所占的空间是7个字节
	printf("%d\n", sizeof(arr));
	
	// 4 这里的arr是首元素的地址,arr+0还是首元素的地址。地址的大小是4个字节
	printf("%d\n", sizeof(arr + 0));
	
	// 1 这里的arr是首元素的地址,*arr表示首元素。首元素所占的空间是1个字节
	printf("%d\n", sizeof(*arr));
	
	// 1 第2个元素所占的空间是1个字节
	printf("%d\n", sizeof(arr[1]));
	
	// 4 满足以上条件->&数组名:数组名表示整个数组。但是数组的地址也是地址,地址的大小是4个字节
	printf("%d\n", sizeof(&arr));
	
	// 4 满足以上条件->& 数组名:数组名表示整个数组。&arr + 1是跳过整个数组后的地址,但也是地址。地址的大小是4个字节
	printf("%d\n", sizeof(&arr + 1));
	
	// &arr[0]表示首元素的地址,&arr[0] + 1是第二个元素的地址。地址的大小是4个字节
	printf("%d\n", sizeof(&arr[0] + 1));

	// 6 这里的arr是首元素的地址,strlen函数会从这个地址,向后面数数,直到遇到\0,
	printf("%d\n", strlen(arr));
	
	// 6 这里的arr是首元素的地址,arr + 0 还是首元素的地址,strlen函数会从这个地址,向后面数数,直到遇到\0,
	printf("%d\n", strlen(arr + 0));
	
	// 报错 这里的arr表示首元素的地址,*arr表示解引用,找到了首元素'a','a'对应ascll码表十进制的值为97,
	// 那么strlen函数就把97当作起始地址值,开始准备从这里向后面数数。那么自然而然就会出现错误,即非法访问内存了
	//printf("%d\n", strlen(*arr));
	
	// 报错 理由同上
	//printf("%d\n", strlen(arr[1]));
	
	// 6 满足以上条件->& 数组名:数组名表示整个数组。strlen函数会从起始地址开始向后数数,直到遇到\0
	printf("%d\n", strlen(&arr));
	
	// 随机值 满足以上条件->& 数组名:数组名表示整个数组。
	// &arr + 1是跳过整个数组后的地址,但也是地址。
	// 地址的大小是4个字节,可是我们求的不是地址大小,而是元素的个数,strlen函数会从跳过整个数组后的地址开始向后数数
	// 然而这个地址处和后面没有元素,所以会出现随机值
	printf("%d\n", strlen(&arr + 1));
	
	// 5 &arr[0]首元素的地址,&arr[0] + 1 第2个元素的地址。strlen函数会从这个地址开始向后面数数,直到遇到\0
	printf("%d\n", strlen(&arr[0] + 1));
	
	return 0;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

在这里插入图片描述

c语言 指针精髓小题4
#include <stdio.h>

int main() {

	// 以下为32位平台测试
	char* p = "abcdef";
	
	// 4 p是一个指针变量, sizeof(p)计算指针变量的大小
	// 4 也可以这里理解,p指向首元素的地址(注意用词:指向),不管是什么类型的地址,地址的大小都是4个字节
	printf("%d\n", sizeof(p));
	
	// 4 p指向首元素的地址,p+1指向第二个字符的地址,不管是什么类型的地址,地址的大小都是4个字节
	printf("%d\n", sizeof(p + 1));
	
	// 1 p指向首元素的地址,*p表示解引用,找到字符'a', 字符'a'所占的空间是一个字节
	printf("%d\n", sizeof(*p));
	
	// 1 p[0] == *(p+0) p指向首元素地址,p+0还是指向首元素的地址,*(p+0)表示解引用,找到字符'a', 字符'a'所占的空间是一个字节
	printf("%d\n", sizeof(p[0]));
	
	// 4 含义如下图 &p:表示地址,不管是什么类型的地址,地址的大小都是4个字节
	printf("%d\n", sizeof(&p));
	// 4 含义如下图 &p+1:表示地址,不管是什么类型的地址,地址的大小都是4个字节
	printf("%d\n", sizeof(&p + 1));
	
	// 4 含义如下图 p[0]表示字符'a',&p[0]表示找到字符'a'的地址,&p[0]+1表示找到字符'b'的地址,不管是什么类型的地址,地址的大小都是4个字节
	printf("%d\n", sizeof(&p[0] + 1));

	// 6 p指向首元素的地址,strlen函数从这个地址处,向后面数数,直到遇到\0
	printf("%d\n", strlen(p));
	
	// 5 p指向首元素的地址,p+1指向第二个字符的地址,strlen函数从这个地址处,向后面数数,直到遇到\0
	printf("%d\n", strlen(p + 1));

	// 报错 p指向首元素的地址,*p表示解引用,找到了首元素'a','a'对应ascll码表十进制的值为97,
	// 那么strlen函数就把97当作起始地址值,开始准备从这里向后面数数。那么自然而然就会出现错误,即非法访问内存了
	//printf("%d\n", strlen(*p));
	
	// 报错 理由同上
	//printf("%d\n", strlen(p[0]));
	
	// 随机值 &p表示起始地址,strlen函数,会从该处,开始向后面数数,但因为p里面存放的是随机地址,这个随机地址中什么时候出现\0不确定,所以才为随机值
	printf("%d\n", strlen(&p));
	
	// 随机值 理由同上
	printf("%d\n", strlen(&p + 1));
	
	// 5 p[0]表示字符'a',&p[0]表示找到字符'a'的地址,&p[0]+1表示找到字符'b'的地址,strlen函数从此处开始向后数数,直到遇到\0
	printf("%d\n", strlen(&p[0] + 1));
	
	return 0;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

在这里插入图片描述
在这里插入图片描述

c语言 指针精髓小题5
#include <stdio.h>

int main() {

	// 二维数组
	int a[3][4] = { 0 };

	// 48 sizeof(数组名):数组名表示整个数组
	printf("%d\n", sizeof(a));
	// 4
	printf("%d\n", sizeof(a[0][0]));
	// 16 a[0]相当于第一行一维数组的数组名,sizeof(a[0])计算的是第一行的大小
	printf("%d\n", sizeof(a[0]));
	// 4 a[0]是第一行的数组名,数组名此时是第一行首元素的地址,a[0]+1是第一行第二个元素的地址,地址的大小是4个字节
	printf("%d\n", sizeof(a[0] + 1));
	// 4 *(a[0] + 1))第一行第二个元素的所占空间为4个字节
	printf("%d\n", sizeof(*(a[0] + 1)));
	// 4 a是二维数组的数组名,没有sizeof(a),也没有&a,所以a是首元素地址,
	// 二维数组的首元素是他的第一行,a就是第一行元素的地址,a+1就是第二行首元素的地址
	printf("%d\n", sizeof(a + 1));
	// 16 a+1就是第二行首元素的地址,*(a[0] + 1))是第二行元素的大小
	printf("%d\n", sizeof(*(a + 1)));
	// &a[0] + 1第二行的地址
	printf("%d\n", sizeof(&a[0] + 1));
	// *(&a[0] + 1))第二行的大小
	printf("%d\n", sizeof(*(&a[0] + 1)));
	// 16 a是首元素的地址,第一行的地址。*a就是第一行,sizeof(*a)计算第一行的大小
	printf("%d\n", sizeof(*a));
	// 16 sizeof函数计算的是类型的大小,即计算的是存放了4个整型数的一维数组的大小,与该数组是否越界无关。
	printf("%d\n", sizeof(a[3]));

	return 0;
}
/*
	int a[2][3] = { 1, 2, 3, 4, 5 ,6 };
	printf("%p\n",&a[0][1]);
	printf("%p\n",a[0]+1);

	printf("%p\n", &a[1][1]);
	printf("%p\n", a[1] + 1);
*/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/223548
推荐阅读
相关标签
  

闽ICP备14008679号