Diretório de artigos
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;
}
int main()
{
char* p = "abcde";
*p = 'q';//常量字符串是不可以修改的
printf("%c\n", *p);
printf("%s\n", p);
return 0;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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.