Revisitando ponteiros (explicação detalhada dos tipos de ponteiro)


prefácio

Os ponteiros são uma parte difícil da linguagem C e você precisa aprender, pensar, aprender e pensar repetidamente. Os ponteiros de aprendizado podem basicamente compreender a essência do nível de estrutura de dados da linguagem C.

1. Ponteiro de caractere

int main()
{
    
    
	char* p = "abcde";//"abcde"是一个常量字符串
	printf("%c\n", *p);//p存放的是常量字符串的首地址
	printf("%s\n", p);
	return 0;
}

insira a descrição da imagem aqui

int main()
{
    
    
	char* p = "abcde";
	*p = 'q';//常量字符串是不可以修改的
	printf("%c\n", *p);
	printf("%s\n", p);
	return 0;
}

insira a descrição da imagem aqui
Strings constantes não podem ser modificadas. Embora o programa não tenha gerado erros e tenha sido gerado normalmente, o programa travou em tempo de execução. , geralmente adicionamos modificação const antes da string constante .

int main()
{
    
    
	const char* p = "abcde";
	*p = 'q';//常量字符串是不可以修改的
	printf("%c\n", *p);
	printf("%s\n", p);
	return 0;
}

insira a descrição da imagem aqui
Adicionar a modificação const nos impedirá de manipular strings constantes e evitar travamentos do programa.
Vamos dar uma olhada mais profunda nos ponteiros de caracteres por meio de um caso:

int main()
{
    
    
	char ch1[] = "abcde";
	char ch2[] = "abcde";
	const char* p1 = "abcde";
	const char* p2 = "abcde";
	if (ch1 == ch2)
	{
    
    
		printf("比较的是数组的元素!\n");
	}
	else
	{
    
    
		printf("比较的是数组的地址!\n");
	}
	if (p1 == p2)
	{
    
    
		printf("比较的是指针的地址!\n");
	}
	else
	{
    
    
		printf("比较的是指针的元素!\n");
	}
	if (!strcmp(ch1, ch2))
	{
    
    
		printf("比较的是数组的元素!\n");
	}
	else
	{
    
    
		printf("比较的是数组的地址!\n");
	}
	return 0;
}

insira a descrição da imagem aqui
Comparando os resultados, podemos ver que ao comparar diretamente com o nome do array, o endereço é comparado , e o nome do array representa o endereço do primeiro elemento do array, então o resultado impresso é o endereço de comparação, mas a comparação direta de the pointers mostra que os endereços dos dois ponteiros são os mesmos, porque os dois ponteiros apontam para a mesma string constante, então os dois ponteiros apontam para o mesmo espaço alocado . Para comparar os elementos de duas matrizes, use a função strcmp(). Se os resultados das duas matrizes forem iguais, retorne 0 e, se os resultados forem diferentes, retorne diferente de zero .

2. Ponteiro de matriz

Os ponteiros de matriz, como o nome indica, são matrizes e os ponteiros de matriz são usados ​​para armazenar ponteiros .

int main()
{
    
    
	int arr[5] = {
    
     0 };//整形数组,里面含有5个整形元素
	char ch[5] = {
    
     0 };//字符数组,里面含有5个字符元素
	int* parr[5];//整形数组指针,里面含有5个整形指针
	char* pch[5];//字符数组指针,里面含有5个字符指针
	return 0;
}

O uso geral é o seguinte:

int main()
{
    
    
	int arr1[] = {
    
     1,2,3 };
	int arr2[] = {
    
     4,5,6 };
	int arr3[] = {
    
     7,8,9 };
	int* parr[] = {
    
     arr1,arr2,arr3 };
	for (int i = 0; i < 3; i++)
	{
    
    
		for (int j = 0; j < 3; j++)
		{
    
    
			printf("%d ", *(parr[i] + j));//j表示整形数组的元素
		}
		printf("\n");
	}
	return 0;
}

insira a descrição da imagem aqui

3. Matriz de ponteiros

Uma matriz de ponteiros, como o nome indica, é um ponteiro, e uma matriz de ponteiros é um ponteiro para uma matriz .

int main()
{
    
    
	int* pi = NULL;//pi是整形指针,用来存放整形的地址,指向整形元素
	char* pc = NULL;//pc是字符指针,用来存放字符的地址,指向字符元素
	int arr[5] = {
    
     1,2,3,4,5 };
	int(*parr)[5] = &arr;//&arr是数组的地址,[]的优先级高与*,*和parr结合,代表parr是一个指针
	return 0;
}

A prioridade de [ ] é combinada com *, * e parr, indicando que parr é um ponteiro, e então combinada com [ ], indicando que aponta para um array.

int main()
{
    
    
	int arr[10] = {
    
     1,2,3,4,5,6,7,8,9,10 };
	int* p = arr;//p指向arr数组的首元素
	for (int i = 0; i < 10; i++)
	{
    
    
		printf("%d ", *(p + i));//指针偏移i个位置
		printf("%d ", p[i]);
		printf("%d ", *(arr + i));//arr代表首元素地址,偏移i个位置
		printf("%d ", arr[i]);
		printf("\n");
	}
	return 0;
}

insira a descrição da imagem aqui
Podemos ver que os arrays podem ser usados ​​como ponteiros, e os ponteiros também podem acessar elementos do array por meio de subscritos.
Vamos dar uma olhada mais profunda nas matrizes de ponteiros por meio de um caso:

void print1(int arr[3][3])
{
    
    
	for (int i = 0; i < 3; i++)
	{
    
    
		for (int j = 0; j < 3; j++)
		{
    
    
			printf("%d ", arr[i][j]);//j表示整形数组的元素
			//printf("%d ", *(arr[i] + j));//数组当指针用
			//printf("%d ", *(*(arr + i) + j));//数组当指针用
		}
		printf("\n");
	}
}
void print2(int(*p)[3])
{
    
    
	for (int i = 0; i < 3; i++)
	{
    
    
		for (int j = 0; j < 3; j++)
		{
    
    
			printf("%d ", *(*(p + i) + j));
			//p+i表示第几行元素,通过解引用获得这一行元素下标。
			//+j表示找到这一行的第j个元素,通过解引用获得这一个元素。
			//printf("%d ", *(p[i] + j));//指针当数组用
			//printf("%d ", p[i][j]);//指针当数组用
		}
		printf("\n");
	}
}
int main()
{
    
    
	int arr[3][3] = {
    
     {
    
    1,2,3},{
    
    4,5,6},{
    
    7,8,9} };
	int(*p)[3] = &arr;
	print1(arr);//数组首元素地址,数组形式
	printf("\n");
	print2(arr);//指针数组形式
	printf("\n");
	return 0;
}

insira a descrição da imagem aqui
O que significa o seguinte?

int (*p[5])[10];

Primeiro, p é primeiro combinado com [ ], o que significa que p é uma matriz, e p[ ] é retirado e a estrutura se torna int (* ) [10].
O significado do código acima é: o array tem 5 elementos, cada elemento é um array de ponteiros, e cada array de ponteiros aponta para um array contendo 10 elementos inteiros.

4. Ponteiro de função

Um ponteiro de função é um ponteiro para uma função.
Antes de entender os ponteiros de função, vamos dar uma olhada no endereço da função:

int ADD(int x, int y)
{
    
    
	return x + y;
}
int main()
{
    
    
	printf("%p\n", &ADD);//对ADD取地址
	printf("%p\n", ADD);//函数名
	return 0;
}

insira a descrição da imagem aqui

Podemos descobrir que tanto o endereço da função quanto o nome da função representam o endereço da função . Portanto, ambas as formas de chamada de função têm o mesmo resultado.
Vamos dar uma olhada formal nos ponteiros de função:

int ADD(int x, int y)
{
    
    
	return x + y;
}
int main()
{
    
    
	int* p1;//整形指针
	int* p2[5];//数组指针
	int(*p3)[5];//指针数组
	int(*p4)(int ,int);//函数指针
	p4 = &ADD;
	printf("%d\n", (*p4)(2, 5));//通过解引用调用
	printf("%d\n", p4(2, 5));//直接通过函数地址调用,和函数名调用类似
	return 0;
}

insira a descrição da imagem aqui

Comparando os ponteiros que vimos, podemos encontrar:

int main()
{
    
    
	int* p1;//整形指针
	//p1和*结合,代表是一个指针,类型为int
	int* p2[5];//数组指针
	//[ ]的优先级高于*,所以p2先和[ ]结合,代表p2是一个数组,剩下int *,代表存储的类型,为整形指针。
	int(*p3)[5];//指针数组
	//p3先和*结合,代表一个指针,剩下的为int [5],代表一个含有5个整形元素的数组,p3指向这个数组
	int(*p4)(int, int)=&ADD;//函数指针
	//p4先和*结合,代表一个指针,剩下的为int (int,int),代表两个形参都为int类型,返回值也为int类型
}

Usar apenas ponteiros de função para chamar funções é muito complicado e o uso será mencionado na função de retorno de chamada.

5. Matriz de ponteiros de função

A matriz de ponteiros de função é uma matriz de ponteiros e a matriz é usada para armazenar o endereço da função.
Como determinar uma matriz de ponteiros de função?
De acordo com a combinação anterior, basta adicionar [ ] após o nome do ponteiro .

int(*p5[5])(int, int);//函数指针数组
//p5先和[ ]结合,代表p5是一个数组,剩下的为int(*)(int, int),这个是不是很熟悉,剩下这个就是函数指针

Vejamos o uso de matrizes de ponteiros de função por meio de uma calculadora simples:

void test()
{
    
    
	printf("1,相加,2,相减\n");
	printf("3,相乘,4,相除\n");
	printf(" 0,退出程序\n");
}
int ADD(int x, int y)
{
    
    
	return x + y;
}
int SUB(int x, int y)
{
    
    
	return x - y;
}
int MUL(int x, int y)
{
    
    
	return x * y;
}
int DIV(int x, int y)
{
    
    
	return x / y;
}
int main()
{
    
    
	int num = 0;
	int(*p5[5])(int, int) = {
    
    0, ADD,SUB,MUL,DIV };//数组下标从零开始,我们可以把数组下标为0的元素直接初始化为0
	test();
	do
	{
    
    
		int x, y;
		printf("请输入要进行的操作:");
		scanf("%d", &num);
		switch (num)
		{
    
    
		case 0:
			printf("程序即将退出\n");
			break;
		case 1:
			printf("\n请输入要进行的操作的两个数:");
			scanf("%d %d", &x, &y);
			printf("%d\n", p5[num](x, y));
			break;
		case 2:
			printf("\n请输入要进行的操作的两个数:");
			scanf("%d %d", &x, &y);
			printf("%d\n", p5[num](x, y));
			break;
		case 3:
			printf("\n请输入要进行的操作的两个数:");
			scanf("%d %d", &x, &y);
			printf("%d\n", p5[num](x, y));
			break;
		case 4:
			printf("\n请输入要进行的操作的两个数:");
			scanf("%d %d", &x, &y);
			printf("%d\n", p5[num](x, y));
			break;
		default:
			printf("输入错误,请重新输入!\n");
			break;
		}
	} while (num);
	return 0;
}

insira a descrição da imagem aqui
Podemos chamar diretamente a função correspondente com o número de entrada, mas descobrimos que alguns códigos neste código são redundantes, então podemos encapsular o código redundante em uma função.

6. Um ponteiro para uma matriz de ponteiros de função

Um ponteiro para uma matriz de ponteiros de função é um ponteiro, e o ponteiro aponta para uma matriz cujos elementos são ponteiros de função.
Após a derivação dos ponteiros acima, podemos facilmente escrever um ponteiro para um array de ponteiros de função:

int(*(*p6)[5])(int, int);//指向函数指针数组的指针
//p6先和*结合,代表p5是一个指针,剩下的为int(* [ ])(int, int)就是函数指针数组
//p6是一个数组指针,指向的的数组有5个元素。
//每个元素的类型是一个函数指针类型int(*)(int,int)

7. Função de retorno de chamada

Uma função de retorno de chamada é uma função chamada por meio de um ponteiro de função . Se um ponteiro de função (endereço) é passado como parâmetro para outra função, quando esse ponteiro é usado para chamar a função para a qual aponta, dizemos que se trata de uma função callback. A função de retorno de chamada não é chamada diretamente pelo método de implementação da função, mas é chamada por outra parte quando ocorre um evento ou condição específica e é usada para responder ao evento ou condição. Em seguida, usamos nossa própria classificação para imitar a função qsort
na função de biblioteca .

void Swap(char* p1, char* p2,int width)//交换两个元素的值
{
    
    
	for (int i = 0; i < width; i++)
	{
    
    
		char ch = *p1;
		*p1 = *p2;
		*p2 = ch;
		p1++;
		p2++;
	}
}
int cmp_int(void* p1,void* p2)//整形比较方法
{
    
    
	return *((int*)p1) - *((int*)p2);
}
int cmp_float(void* p1, void* p2)//浮点型比较方法
{
    
    
	if (*(float*)p1 - *(float*)p2 > 0)
	{
    
    
		return 1;
	}
	else if (*(float*)p1 - *(float*)p2 == 0)
	{
    
    
		return 0;
	}
	else
	{
    
    
		return -1;
	}
}
void Bubble_sort(void *arr,int size,int width,int (*p)(const void *p1, const void* p2))//排序函数
{
    
    
	for (int i = 0; i < size - 1; i++)
	{
    
    
		for (int j = 0; j < size - i - 1; j++)
		{
    
    
			if (p((char*)arr + j * width, (char*)arr + (j + 1) * width) > 0)
			//把void*强制转化为char*,通过width来增加地址。
			{
    
    
				Swap((char*)arr + j * width, (char*)arr + (j + 1) * width, width);
				//对要交换元素一个字节一个字节进行交换
			}
		}
	}
}
void test1()
{
    
    
	int arr[10] = {
    
     1,5,9,7,3,8,4,2,6,0 };
	int size = sizeof(arr) / sizeof(arr[0]);
	Bubble_sort(arr,size,sizeof(int), cmp_int);
	//第一个参数:待排序数组的首元素地址
	//第二个参数:待排序的元素个数
	//第三个参数:待排序数组中每个元素的大小
	//第四个参数:函数指针,用来比较两个元素的所用函数的地址-需要我们自己实现。
	//函数指针的两个参数是待比较的两个元素的地址
	for (int i = 0; i < size; i++)
	{
    
    
		printf("%d\t", arr[i]);
	}
	printf("\n");
}
void test2()
{
    
    
	float farr[10] = {
    
     1.1,5.5,9.9,7.7,3.3,8.8,4.4,2.2,6.6,0.5 };
	int size = sizeof(farr) / sizeof(farr[0]);
	Bubble_sort(farr, size, sizeof(float), cmp_float);
	for (int i = 0; i < size; i++)
	{
    
    
		printf("%.1f\t", farr[i]);
	}
}
int main()
{
    
    
	test1();//整形排序
	test2();//浮点形排序
	return 0;
}

insira a descrição da imagem aqui
Implementamos a classificação de ponto flutuante e inteira por meio de uma função Bubble_sort própria. Como a função Bubble_sort não sabe que tipo de array deve ser classificado, a função que classificamos usa void * para receber (void * pode receber qualquer tipo de endereço, mas void * não pode ser desreferenciado diretamente. Portanto, o tipo deve ser forçado quando conversão de classificação) . Quando usamos nossa função de classificação para classificar, sabemos o método para comparar, então temos que construir nossas próprias funções de comparação cmp_int e cmp_float, se quisermos classificar matrizes de caracteres, podemos escrever uma função de comparação de caracteres, por meio de Bubble_sort para fazer o ligue . Ao chamar a função de comparação, converta void * para char *, e aumente o endereço pela largura, pois a função de ordenação não sabe o tipo de dado que queremos ordenar, podemos usar char para fazer o byte 1, mais o tamanho do personagem que queremos comparar, para determinar a posição de cada elemento.
Pode ser visto no exemplo acima que não chamamos diretamente a função de comparação que escrevemos, mas chamamos o ponteiro de função por meio da função de classificação, que é a chamada função de retorno de chamada.

Resumir

Vejamos um exemplo para ter uma compreensão mais profunda do conteúdo do ponteiro.

void (*test(int, void (*)(int)))(int);
//test是一个函数声明
//test函数的参数有2个,第一个是int,第二个是函数指针,该函数指针指向的函数的参数是int,返回值为void
//test函数的返回值也是一个函数指针,该函数指针指向的函数的参数为int,返回值为void
typedef void(*fun)(int);//对void(*)(int)重命名,fun要跟着*,不可以写在最后。
fun test(int, fun);//简化版本

Um soldado do sol acabará exausto e a contribuição acabará indo para o mar. Os ponteiros exigem que aprendamos e pensemos repetidamente.

Acho que você gosta

Origin blog.csdn.net/2301_76986069/article/details/130446613
Recomendado
Clasificación