在C语言中什么时候应该使用双重间接?谁能举个例子解释一下吗?
我所知道的是双间接是指向指针的指针。为什么我需要指向指针的指针?
在C语言中什么时候应该使用双重间接?谁能举个例子解释一下吗?
我所知道的是双间接是指向指针的指针。为什么我需要指向指针的指针?
当前回答
下面是一个非常简单的c++示例,说明如果要使用函数将指针设置为指向对象,则需要一个指针指向指针。否则,指针将继续返回null。
(一个c++的答案,但我相信在C中也是一样的)
(同样,供参考:谷歌("pass by value c++") = "默认情况下,c++中的参数是按值传递的。当实参按值传递时,实参的值被复制到函数的形参中。”)
我们想让指针b等于字符串a。
#include <iostream>
#include <string>
void Function_1(std::string* a, std::string* b) {
b = a;
std::cout << (b == nullptr); // False
}
void Function_2(std::string* a, std::string** b) {
*b = a;
std::cout << (b == nullptr); // False
}
int main() {
std::string a("Hello!");
std::string* b(nullptr);
std::cout << (b == nullptr); // True
Function_1(&a, b);
std::cout << (b == nullptr); // True
Function_2(&a, &b);
std::cout << (b == nullptr); // False
}
// Output: 10100
在Function_1(&a, b);这条线上会发生什么?
The "value" of &main::a (an address) is copied into the parameter std::string* Function_1::a. Therefore Function_1::a is a pointer to (i.e. the memory address of) the string main::a. The "value" of main::b (an address in memory) is copied into the parameter std::string* Function_1::b. Therefore there are now 2 of these addresses in memory, both null pointers. At the line b = a;, the local variable Function_1::b is then changed to equal Function_1::a (= &main::a), but the variable main::b is unchanged. After the call to Function_1, main::b is still a null pointer.
在函数_2(&a, &b);这一行发生了什么?
The treatment of the a variable is the same: within the function, Function_2::a is the address of the string main::a. But the variable b is now being passed as a pointer to a pointer. The "value" of &main::b (the address of the pointer main::b) is copied into std::string** Function_2::b. Therefore within Function_2, dereferencing this as *Function_2::b will access and modify main::b . So the line *b = a; is actually setting main::b (an address) equal to Function_2::a (= address of main::a) which is what we want.
如果你想用一个函数来修改一个东西,无论是一个对象还是一个地址(指针),你必须传递一个指向那个东西的指针。您实际传入的内容不能被修改(在调用范围内),因为创建了本地副本。
(一个例外是如果形参是一个引用,例如std::string& a.但通常这些是const。一般来说,如果你调用f(x),如果x是一个对象,你应该能够假设f不会修改x。但如果x是一个指针,那么你应该假设f可能修改x指向的对象。)
其他回答
这里的大多数答案或多或少都与应用程序编程有关。下面是一个嵌入式系统编程的例子。例如,以下是NXP Kinetis KL13系列微控制器参考手册的摘录,此代码片段用于从固件中运行驻留在ROM中的引导加载程序:
" 为了获得入口点的地址,用户应用程序读取包含引导加载程序API树指针的单词,该指针位于引导加载程序向量表的0x1C偏移量处。向量表被放置在引导加载器地址范围的底部,ROM的地址范围是0x1C00_0000。因此,API树指针位于地址0x1C00_001C。
引导加载程序API树是一个包含指向其他结构的指针的结构,这些结构具有引导加载程序的函数和数据地址。引导加载程序入口点总是API树的第一个单词。 "
uint32_t runBootloaderAddress;
void (*runBootloader)(void * arg);
// Read the function address from the ROM API tree.
runBootloaderAddress = **(uint32_t **)(0x1c00001c);
runBootloader = (void (*)(void * arg))runBootloaderAddress;
// Start the bootloader.
runBootloader(NULL);
例如,你可能想要确保当你释放某个东西的内存时,你将指针设置为空。
void safeFree(void** memory) {
if (*memory) {
free(*memory);
*memory = NULL;
}
}
当你调用这个函数时,你会用指针的地址来调用它
void* myMemory = someCrazyFunctionThatAllocatesMemory();
safeFree(&myMemory);
现在myMemory被设置为NULL,任何重用它的尝试都将是非常明显的错误。
我经常使用它们的一件事是,当我有一个对象数组,我需要根据不同的字段对它们执行查找(二进制搜索)。 我保留原始数组…
int num_objects;
OBJECT *original_array = malloc(sizeof(OBJECT)*num_objects);
然后创建一个指向对象的排序指针数组。
int compare_object_by_name( const void *v1, const void *v2 ) {
OBJECT *o1 = *(OBJECT **)v1;
OBJECT *o2 = *(OBJECT **)v2;
return (strcmp(o1->name, o2->name);
}
OBJECT **object_ptrs_by_name = malloc(sizeof(OBJECT *)*num_objects);
int i = 0;
for( ; i<num_objects; i++)
object_ptrs_by_name[i] = original_array+i;
qsort(object_ptrs_by_name, num_objects, sizeof(OBJECT *), compare_object_by_name);
您可以根据需要创建任意数量的已排序指针数组,然后对已排序指针数组使用二进制搜索,根据已有的数据访问所需的对象。对象的原始数组可以保持无序,但是每个指针数组将按照它们指定的字段进行排序。
比较变量的修改值和指针的修改值:
#include <stdio.h>
#include <stdlib.h>
void changeA(int (*a))
{
(*a) = 10;
}
void changeP(int *(*P))
{
(*P) = malloc(sizeof((*P)));
}
int main(void)
{
int A = 0;
printf("orig. A = %d\n", A);
changeA(&A);
printf("modi. A = %d\n", A);
/*************************/
int *P = NULL;
printf("orig. P = %p\n", P);
changeP(&P);
printf("modi. P = %p\n", P);
free(P);
return EXIT_SUCCESS;
}
这帮助我避免指针被调用函数修改时返回指针的值(用于单链表)。
古老的(坏的):
int *func(int *P)
{
...
return P;
}
int main(void)
{
int *pointer;
pointer = func(pointer);
...
}
新(更好的):
void func(int **pointer)
{
...
}
int main(void)
{
int *pointer;
func(&pointer);
...
}
有点晚了,但希望这能帮助到一些人。
在C语言中,数组总是在堆栈上分配内存,因此函数不能返回 一个(非静态)数组,因为内存分配在堆栈上 当执行到达当前块的末尾时自动释放。 当你想处理二维数组时,这真的很烦人 (即矩阵),并实现一些可以改变和返回矩阵的函数。 要实现这一点,可以使用指针对指针来实现矩阵 动态分配内存:
/* Initializes a matrix */
double** init_matrix(int num_rows, int num_cols){
// Allocate memory for num_rows float-pointers
double** A = calloc(num_rows, sizeof(double*));
// return NULL if the memory couldn't allocated
if(A == NULL) return NULL;
// For each double-pointer (row) allocate memory for num_cols floats
for(int i = 0; i < num_rows; i++){
A[i] = calloc(num_cols, sizeof(double));
// return NULL if the memory couldn't allocated
// and free the already allocated memory
if(A[i] == NULL){
for(int j = 0; j < i; j++){
free(A[j]);
}
free(A);
return NULL;
}
}
return A;
}
这里有一个例子:
double** double* double
------------- ---------------------------------------------------------
A ------> | A[0] | ----> | A[0][0] | A[0][1] | A[0][2] | ........ | A[0][cols-1] |
| --------- | ---------------------------------------------------------
| A[1] | ----> | A[1][0] | A[1][1] | A[1][2] | ........ | A[1][cols-1] |
| --------- | ---------------------------------------------------------
| . | .
| . | .
| . | .
| --------- | ---------------------------------------------------------
| A[i] | ----> | A[i][0] | A[i][1] | A[i][2] | ........ | A[i][cols-1] |
| --------- | ---------------------------------------------------------
| . | .
| . | .
| . | .
| --------- | ---------------------------------------------------------
| A[rows-1] | ----> | A[rows-1][0] | A[rows-1][1] | ... | A[rows-1][cols-1] |
------------- ---------------------------------------------------------
The double-pointer-to-double-pointer A points to the first element A[0] of a memory block whose elements are double-pointers itself. You can imagine these double-pointers as the rows of the matrix. That's the reason why every double-pointer allocates memory for num_cols elements of type double. Furthermore A[i] points to the i-th row, i.e. A[i] points to A[i][0] and that's just the first double-element of the memory block for the i-th row. Finally, you can access the element in the i-th row and j-th column easily with A[i][j].
下面是一个完整的例子来演示它的用法:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
/* Initializes a matrix */
double** init_matrix(int num_rows, int num_cols){
// Allocate memory for num_rows double-pointers
double** matrix = calloc(num_rows, sizeof(double*));
// return NULL if the memory couldn't allocated
if(matrix == NULL) return NULL;
// For each double-pointer (row) allocate memory for num_cols
// doubles
for(int i = 0; i < num_rows; i++){
matrix[i] = calloc(num_cols, sizeof(double));
// return NULL if the memory couldn't allocated
// and free the already allocated memory
if(matrix[i] == NULL){
for(int j = 0; j < i; j++){
free(matrix[j]);
}
free(matrix);
return NULL;
}
}
return matrix;
}
/* Fills the matrix with random double-numbers between -1 and 1 */
void randn_fill_matrix(double** matrix, int rows, int cols){
for (int i = 0; i < rows; ++i){
for (int j = 0; j < cols; ++j){
matrix[i][j] = (double) rand()/RAND_MAX*2.0-1.0;
}
}
}
/* Frees the memory allocated by the matrix */
void free_matrix(double** matrix, int rows, int cols){
for(int i = 0; i < rows; i++){
free(matrix[i]);
}
free(matrix);
}
/* Outputs the matrix to the console */
void print_matrix(double** matrix, int rows, int cols){
for(int i = 0; i < rows; i++){
for(int j = 0; j < cols; j++){
printf(" %- f ", matrix[i][j]);
}
printf("\n");
}
}
int main(){
srand(time(NULL));
int m = 3, n = 3;
double** A = init_matrix(m, n);
randn_fill_matrix(A, m, n);
print_matrix(A, m, n);
free_matrix(A, m, n);
return 0;
}