在C语言中什么时候需要使用双重间接?可以举一个例子来解释吗?
我知道双重间接是指指向指针的指针。那么为什么需要指向指针的指针呢?
char *word
如果你想要一个单词列表(一个句子),你可以使用char **sentence
如果你想要一个句子列表(一个独白),你可以使用char ***monologue
如果你想要一个独白列表(一个传记),你可以使用char ****biography
如果你想要一个传记列表(一个生物资料库),你可以使用char *****biolibrary
如果你想要一个生物资料库列表(一个??lol),你可以使用char ******lol
... ...#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int wordsinsentence(char **x) {
int w = 0;
while (*x) {
w += 1;
x++;
}
return w;
}
int wordsinmono(char ***x) {
int w = 0;
while (*x) {
w += wordsinsentence(*x);
x++;
}
return w;
}
int wordsinbio(char ****x) {
int w = 0;
while (*x) {
w += wordsinmono(*x);
x++;
}
return w;
}
int wordsinlib(char *****x) {
int w = 0;
while (*x) {
w += wordsinbio(*x);
x++;
}
return w;
}
int wordsinlol(char ******x) {
int w = 0;
while (*x) {
w += wordsinlib(*x);
x++;
}
return w;
}
int main(void) {
char *word;
char **sentence;
char ***monologue;
char ****biography;
char *****biolibrary;
char ******lol;
//fill data structure
word = malloc(4 * sizeof *word); // assume it worked
strcpy(word, "foo");
sentence = malloc(4 * sizeof *sentence); // assume it worked
sentence[0] = word;
sentence[1] = word;
sentence[2] = word;
sentence[3] = NULL;
monologue = malloc(4 * sizeof *monologue); // assume it worked
monologue[0] = sentence;
monologue[1] = sentence;
monologue[2] = sentence;
monologue[3] = NULL;
biography = malloc(4 * sizeof *biography); // assume it worked
biography[0] = monologue;
biography[1] = monologue;
biography[2] = monologue;
biography[3] = NULL;
biolibrary = malloc(4 * sizeof *biolibrary); // assume it worked
biolibrary[0] = biography;
biolibrary[1] = biography;
biolibrary[2] = biography;
biolibrary[3] = NULL;
lol = malloc(4 * sizeof *lol); // assume it worked
lol[0] = biolibrary;
lol[1] = biolibrary;
lol[2] = biolibrary;
lol[3] = NULL;
printf("total words in my lol: %d\n", wordsinlol(lol));
free(lol);
free(biolibrary);
free(biography);
free(monologue);
free(sentence);
free(word);
}
输出:
我的英雄联盟中总单词数为:243
arr[a][b][c]
不是***arr
。指针的指针使用引用的引用,而arr[a][b][c]
按行主序存储为常规数组。 - MCCCS"my sentence"
, "his sentence"
, "her sentence"
, 和 "their sentence"
,你可以这样做 char *ptr1 = "my sentence";
等等... 但是更方便的方法是创建一个大小为5(4 + NULL)指针的数组:char *ptrs[5] = {"my sentence", "his sentence", ..., NULL}
。当你把这个数组传递给函数 (foo(ptrs)
) 时,数组会自动转换为类型 char**
! - pmg一个原因是您想在函数参数中传递指针并更改其值,为此您需要指向指针的指针。
简单来说,当您希望在函数调用之外保留(或保持更改)内存分配或赋值时,请使用**
。(因此,请将具有双指针参数的函数传递给它。)
这可能不是一个非常好的例子,但它将向您展示基本用法:
#include <stdio.h>
#include <stdlib.h>
void allocate(int **p)
{
*p = (int *)malloc(sizeof(int));
}
int main()
{
int *p = NULL;
allocate(&p);
*p = 42;
printf("%d\n", *p);
free(p);
}
allocate
是void allocate(int *p)
,而你调用它时使用allocate(p)
,那么不会有任何不同。 - Incertezapointer1 = pointer2
来实现,从而将pointer1的地址设置为pointer2的地址。但如果您在函数内部执行此操作,并且希望结果在函数完成后仍然存在,则需要进行一些额外的工作。您需要一个新的指针3来指向pointer1。将pointer3传递给函数。
下面是一个例子,请先查看下面的输出以了解情况。
#include <stdio.h>
int main()
{
int c = 1;
int d = 2;
int e = 3;
int * a = &c;
int * b = &d;
int * f = &e;
int ** pp = &a; // pointer to pointer 'a'
printf("\n a's value: %x \n", a);
printf("\n b's value: %x \n", b);
printf("\n f's value: %x \n", f);
printf("\n can we change a?, lets see \n");
printf("\n a = b \n");
a = b;
printf("\n a's value is now: %x, same as 'b'... it seems we can, but can we do it in a function? lets see... \n", a);
printf("\n cant_change(a, f); \n");
cant_change(a, f);
printf("\n a's value is now: %x, Doh! same as 'b'... that function tricked us. \n", a);
printf("\n NOW! lets see if a pointer to a pointer solution can help us... remember that 'pp' point to 'a' \n");
printf("\n change(pp, f); \n");
change(pp, f);
printf("\n a's value is now: %x, YEAH! same as 'f'... that function ROCKS!!!. \n", a);
return 0;
}
void cant_change(int * x, int * z){
x = z;
printf("\n ----> value of 'a' is: %x inside function, same as 'f', BUT will it be the same outside of this function? lets see\n", x);
}
void change(int ** x, int * z){
*x = z;
printf("\n ----> value of 'a' is: %x inside function, same as 'f', BUT will it be the same outside of this function? lets see\n", *x);
}
这里是输出结果: (请先阅读本文)
a's value: bf94c204
b's value: bf94c208
f's value: bf94c20c
can we change a?, lets see
a = b
a's value is now: bf94c208, same as 'b'... it seems we can, but can we do it in a function? lets see...
cant_change(a, f);
----> value of 'a' is: bf94c20c inside function, same as 'f', BUT will it be the same outside of this function? lets see
a's value is now: bf94c208, Doh! same as 'b'... that function tricked us.
NOW! lets see if a pointer to a pointer solution can help us... remember that 'pp' point to 'a'
change(pp, f);
----> value of 'a' is: bf94c20c inside function, same as 'f', BUT will it be the same outside of this function? lets see
a's value is now: bf94c20c, YEAH! same as 'f'... that function ROCKS!!!.
在 Asha 的回答基础上,如果你使用单个指针来表示下面的示例(例如,alloc1()),则会丢失函数内分配的内存的引用。
#include <stdio.h>
#include <stdlib.h>
void alloc2(int** p) {
*p = (int*)malloc(sizeof(int));
**p = 10;
}
void alloc1(int* p) {
p = (int*)malloc(sizeof(int));
*p = 10;
}
int main(){
int *p = NULL;
alloc1(p);
//printf("%d ",*p);//undefined
alloc2(&p);
printf("%d ",*p);//will print 10
free(p);
return 0;
}
发生这种情况的原因是在alloc1
中指针是按值传递的。因此,当它被重新赋值为alloc1
内部malloc
调用的结果时,更改不涉及不同作用域中的代码。
free(p)
,还需要if(p) free(*p)
。 - Shijing Lv*p
评估为一个值为10的 int
,将其传递给 free()
是个坏主意。 - alkalloc1()
函数中的内存分配引入了内存泄漏。由于从函数返回,指向要释放的指针值已经丢失。 - alkint* alloc1(int* p) { p = (int*)malloc(sizeof(int)); *p = 10; return p}
当然没有意义,因为你不需要向这样的函数传递任何参数。 - Serhii1. 基本概念 -
当您声明如下内容时:-
1. char *ch - (称为字符指针)
- ch 包含单个字符的地址
- (*ch) 将解引用为字符的值。
2. char **ch -
'ch' 包含字符指针数组的地址。(与 1 中相同)
'*ch' 包含单个字符的地址。(注意,由于声明方式不同,它与 1 不同)。
(**ch) 将解引用为字符的确切值。
增加更多指针会扩展数据类型的维度,从字符到字符串,到字符串数组等等... 您可以将其与 1d、2d、3d 矩阵进行关联。
因此,指针的使用取决于您如何声明它。
这是一个简单的代码示例:
int main()
{
char **p;
p = (char **)malloc(100);
p[0] = (char *)"Apple"; // or write *p, points to location of 'A'
p[1] = (char *)"Banana"; // or write *(p+1), points to location of 'B'
cout << *p << endl; //Prints the first pointer location until it finds '\0'
cout << **p << endl; //Prints the exact character which is being pointed
*p++; //Increments for the next string
cout << *p;
}
2. 双指针的另一种应用 -
(这也涵盖了传引用)
假设你想要从一个函数中更新一个字符。如果你尝试以下操作:-
void func(char ch)
{
ch = 'B';
}
int main()
{
char ptr;
ptr = 'A';
printf("%c", ptr);
func(ptr);
printf("%c\n", ptr);
}
输出将会是AA。这个方法不起作用,因为你已经将参数按值传递给函数。
正确的做法是 -
void func( char *ptr) //Passed by Reference
{
*ptr = 'B';
}
int main()
{
char *ptr;
ptr = (char *)malloc(sizeof(char) * 1);
*ptr = 'A';
printf("%c\n", *ptr);
func(ptr);
printf("%c\n", *ptr);
}
现在将这个要求扩展到更新字符串而不是字符。
为此,您需要将参数作为双指针在函数中接收。
void func(char **str)
{
strcpy(str, "Second");
}
int main()
{
char **str;
// printf("%d\n", sizeof(char));
*str = (char **)malloc(sizeof(char) * 10); //Can hold 10 character pointers
int i = 0;
for(i=0;i<10;i++)
{
str = (char *)malloc(sizeof(char) * 1); //Each pointer can point to a memory of 1 character.
}
strcpy(str, "First");
printf("%s\n", str);
func(str);
printf("%s\n", str);
}
在这个例子中,该方法期望一个双指针作为参数以更新字符串的值。
#include
int main()
{
char* ptr = NULL;
ptr = (char*) malloc(255); // 分配一些内存
strcpy(ptr, "Stack Overflow Rocks..!!");
printf("%s\n", ptr);
printf("%d\n", strlen(ptr));
free(ptr);
return 0;
}
但也可以不使用双指针。 - kumarchar
指针数组的第一个元素的地址。一个指向 char*
数组的指针的类型可以像这样定义:char(*(*p)[42])
,它将 p
定义为指向 42 个指向 char
的指针的数组的指针。 - alkmalloc(sizeof(char) * 10);
不是为了分配10个指向 char
的指针,而是只为10个 char
分配空间。 - alkfor(i=0;i<10;i++) { str = ...
没有使用索引 i
。 - alk今天我看到了一个非常好的例子,来自这篇博客文章,我将其总结如下。
假设你有一个用于链表节点的结构体,可能是这样的:
typedef struct node
{
struct node * next;
....
} node;
现在您想实现一个remove_if
函数,它将接受删除条件rm
作为其中一个参数并遍历链表:如果一个条目满足条件(类似于rm(entry)==true
),则其节点将从列表中移除。最后,remove_if
返回链表的头部(可能与原始头部不同)。
您可以编写:
for (node * prev = NULL, * curr = head; curr != NULL; )
{
node * const next = curr->next;
if (rm(curr))
{
if (prev) // the node to be removed is not the head
prev->next = next;
else // remove the head
head = next;
free(curr);
}
else
prev = curr;
curr = next;
}
就像您的for
循环一样。这个消息是:没有双指针,你必须维护一个prev
变量来重新组织指针,并处理两种不同的情况。
但有了双指针,您实际上可以这样写:
// now head is a double pointer
for (node** curr = head; *curr; )
{
node * entry = *curr;
if (rm(entry))
{
*curr = entry->next;
free(entry);
}
else
curr = &entry->next;
}
现在你不需要一个 prev
,因为你可以直接修改 prev->next
指向的内容。
为了让事情更清楚,我们来跟一下代码。在移除时:
entry == *head
:那么就是 *head (==*curr) = *head->next
-- head
现在指向新的头结点的指针。你可以直接改变 head
的内容为一个新的指针来做到这一点。entry != *head
:同样地,*curr
就是 prev->next
所指向的内容,现在它指向了 entry->next
。无论是哪种情况,你都可以用双重指针以一种统一的方式重新组织指针。
#include <stdlib.h>
typedef unsigned char** handle_type;
//some data_structure that the library functions would work with
typedef struct
{
int data_a;
int data_b;
int data_c;
} LIB_OBJECT;
handle_type lib_create_handle()
{
//initialize the handle with some memory that points to and array of 10 LIB_OBJECTs
handle_type handle = malloc(sizeof(handle_type));
*handle = malloc(sizeof(LIB_OBJECT) * 10);
return handle;
}
void lib_func_a(handle_type handle) { /*does something with array of LIB_OBJECTs*/ }
void lib_func_b(handle_type handle)
{
//does something that takes input LIB_OBJECTs and makes more of them, so has to
//reallocate memory for the new objects that will be created
//first re-allocate the memory somewhere else with more slots, but don't destroy the
//currently allocated slots
*handle = realloc(*handle, sizeof(LIB_OBJECT) * 20);
//...do some operation on the new memory and return
}
void lib_func_c(handle_type handle) { /*does something else to array of LIB_OBJECTs*/ }
void lib_free_handle(handle_type handle)
{
free(*handle);
free(handle);
}
int main()
{
//create a "handle" to some memory that the library functions can use
handle_type my_handle = lib_create_handle();
//do something with that memory
lib_func_a(my_handle);
//do something else with the handle that will make it point somewhere else
//but that's invisible to us from the standpoint of the calling the function and
//working with the handle
lib_func_b(my_handle);
//do something with new memory chunk, but you don't have to think about the fact
//that the memory has moved under the hood ... it's still pointed to by the "handle"
lib_func_c(my_handle);
//deallocate the handle
lib_free_handle(my_handle);
return 0;
}
unsigned char
是因为我们要存储指向二进制数据的指针,这些数据将以原始字节形式表示。使用void
类型将需要在某个时候进行强制转换,并且通常不如unsigned char
易读,无法准确表达意图。 - Jason/* 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] |
------------- ---------------------------------------------------------
A
指向一个内存块的第一个元素A[0]
,该内存块的元素本身是双指针。您可以将这些双指针想象为矩阵的行。这就是为什么每个双指针都会为类型为double的num_cols元素分配内存的原因。此外,A[i]
指向第i行,即A[i]
指向A[i][0]
,而这只是第i行内存块中的第一个double元素。最后,您可以很容易地使用A[i][j]
访问第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;
}
int main(int argc, char **argv)
char* c
)和数组表示法(char c[]
)在函数参数中是可以互换的,它们具有完全相同的含义。然而,在函数参数之外,它们是不同的。 - pmg字符串是双指针用法的一个很好的例子。字符串本身是一个指针,所以每当您需要指向一个字符串时,您都需要一个双指针。
double*
。请小心处理。 - Keith Thompson