Answers:
如果您想要一个字符列表(一个单词),则可以使用 char *word
如果您想要单词列表(一个句子),则可以使用 char **sentence
如果您想要一个句子列表(独白),则可以使用 char ***monologue
如果您想要独白列表(传记),则可以使用 char ****biography
如果您需要传记列表(生物图书馆),则可以使用 char *****biolibrary
如果您想要一个生物图书馆列表(大声笑),可以使用 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]
不是a ***arr
。指针的指针使用引用的引用,而指针则按arr[a][b][c]
行主要顺序存储为常规数组。
原因之一是您想更改作为函数参数传递给函数的指针的值,为此,您需要指向指针的指针。
简单地说,使用**
时要保留(或保留在变化)的内存分配或分配甚至外面的函数调用。(因此,请使用双指针arg传递此类函数。)
这可能不是一个很好的示例,但是将向您展示基本用法:
void allocate(int** p)
{
*p = (int*)malloc(sizeof(int));
}
int main()
{
int* p = NULL;
allocate(&p);
*p = 42;
free(p);
}
void allocate(int *p)
并且您将其称为,会有什么不同allocate(p)
?
pointer1 = pointer2
,您就给指针1指针2的地址。但!如果您在函数中执行此操作,并且希望结果在函数完成后仍然存在,则需要做一些额外的工作。您需要一个新的pointer3来指向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()),则将丢失对函数内部分配的内存的引用。
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
指针中按值传递。因此,当将它重新分配给内的malloc
调用结果时alloc1
,所做的更改与其他范围内的代码无关。
free(p)
是不够的,你需要if(p) free(*p)
以及
*p
计算结果为int
保持值为10,将其传递 int
给free()是一个坏主意。
alloc1()
导致内存泄漏。从函数返回将丢失要自由传递的指针值。
今天,我从这篇博客文章中看到了一个很好的例子总结如下。
假设您有一个链表中节点的结构,这可能是
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
。无论在哪种情况下,都可以使用双指针以统一的方式重新组织指针。
1.基本概念-
当您声明如下时:-
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 <stdio.h> int main() { char *ptr = 0; ptr = malloc(255); // allocate some memory strcpy( ptr, "Stack Overflow Rocks..!!"); printf("%s\n", ptr); printf("%d\n",strlen(ptr)); free(ptr); return 0; }
但是您也可以不使用双指针就可以做到这一点。
char
指针数组的第一个元素的地址。char*
例如,将指向数组的指针的类型如下:char(*(*p)[42])
将定义p
为指向42的数组的指针char
。
*str = ...
str
是取消引用的未初始化的,调用未定义的行为。
malloc(sizeof(char) * 10);
不会为10的指针分配空间,char
而char
只为10分配指针
for(i=0;i<10;i++) { str = ...
未使用索引i
。
指针的指针也可以方便地用作内存的“句柄”,您可以在函数之间传递“句柄”以重新定位内存。基本上,这意味着该函数可以更改handle变量内的指针所指向的内存,并且使用该句柄的每个函数或对象都将正确指向新近重新定位(或分配)的内存。库喜欢使用“不透明”的数据类型来执行此操作,也就是说,如果您不必担心它们在使用指向的内存时会做什么,那么只需在两个数据之间传递“句柄”即可库的功能以对该内存执行一些操作...
例如:
#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
将需要在某些时候进行强制转换,并且通常不像要执行的操作那样可读。
int main(int argc, char **argv)
在第二个参数中,它具有:指向char指针的指针。
请注意,指针符号(char* c
)和数组符号(char c[]
)在函数参数中可以互换。所以你也可以写char *argv[]
。换句话说char *argv[]
和char **argv
是可以互换的。
以上表示的实际上是一个字符序列数组(启动时提供给程序的命令行参数)。
有关上述函数签名的更多详细信息,请参见此答案。
char* c
char c[]
函数自变量中的“指针表示法()和数组表示法()可以互换”(并且具有相同的确切含义)。它们是不同的,但是外部函数参数不同。
例如,您可能要确保释放内存时将指针设置为null。
void safeFree(void** memory) {
if (*memory) {
free(*memory);
*memory = NULL;
}
}
调用此函数时,将使用指针地址进行调用
void* myMemory = someCrazyFunctionThatAllocatesMemory();
safeFree(&myMemory);
现在myMemory
设置为NULL,任何重用它的尝试显然都是错误的。
if(*memory)
和free(*memory);
我经常使用它们的一件事是当我有一组对象并且需要通过不同的字段对它们执行查找(二进制搜索)时。
我保留原始数组...
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);
您可以根据需要创建多个排序的指针数组,然后对排序的指针数组使用二进制搜索来通过所拥有的数据访问所需的对象。对象的原始数组可以保持未排序状态,但是每个指针数组将按其指定的字段进行排序。
为什么要使用双指针?
目的是使用函数来更改studentA指向的内容。
#include <stdio.h>
#include <stdlib.h>
typedef struct Person{
char * name;
} Person;
/**
* we need a ponter to a pointer, example: &studentA
*/
void change(Person ** x, Person * y){
*x = y; // since x is a pointer to a pointer, we access its value: a pointer to a Person struct.
}
void dontChange(Person * x, Person * y){
x = y;
}
int main()
{
Person * studentA = (Person *)malloc(sizeof(Person));
studentA->name = "brian";
Person * studentB = (Person *)malloc(sizeof(Person));
studentB->name = "erich";
/**
* we could have done the job as simple as this!
* but we need more work if we want to use a function to do the job!
*/
// studentA = studentB;
printf("1. studentA = %s (not changed)\n", studentA->name);
dontChange(studentA, studentB);
printf("2. studentA = %s (not changed)\n", studentA->name);
change(&studentA, studentB);
printf("3. studentA = %s (changed!)\n", studentA->name);
return 0;
}
/**
* OUTPUT:
* 1. studentA = brian (not changed)
* 2. studentA = brian (not changed)
* 3. studentA = erich (changed!)
*/
以下是一个非常简单的C ++示例,该示例表明,如果要使用函数设置指向对象的指针,则需要指向的指针。否则,指针将继续恢复为null。
(一个C ++答案,但是我相信它在C中是相同的。)
(此外,作为参考:Google(“ 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);
?
&main::a
(地址)的“值” 被复制到参数中std::string* Function_1::a
。因此Function_1::a
是一个指向字符串(即字符串的内存地址)的指针main::a
。
main::b
(内存中的地址)的“值” 被复制到参数中std::string* Function_1::b
。因此,内存中现在有两个地址,均为空指针。在该行上b = a;
,Function_1::b
然后将局部变量更改为等于Function_1::a
(= &main::a
),但该变量main::b
未更改。调用之后Function_1
,main::b
仍然是空指针。
生产线上会发生什么Function_2(&a, &b);
?
a
变量的处理方法是相同的:在函数中,Function_2::a
是字符串的地址main::a
。
但是变量b
现在作为指针传递给指针。&main::b
(指针 的地址main::b
)的“值” 被复制到中std::string** Function_2::b
。因此,在Function_2中,将其取消引用*Function_2::b
将可以访问和修改main::b
。因此,该行*b = a;
实际上是在设置main::b
(地址)等于我们想要的Function_2::a
(=地址main::a
)。
如果要使用函数来修改事物,无论是对象还是地址(指针),都必须传递指向该事物的指针。您实际传递的内容无法修改(在调用范围内),因为已创建本地副本。
(一个例外是,如果参数是一个引用,例如std::string& a
。但是通常是这些const
。通常,如果调用f(x)
,如果x
是对象,则应该能够假定它f
不会被修改x
。但是,如果x
是指针,则应该假设f
可能会修改指向的对象x
。)
晚会晚了一点,但是希望这对某人有帮助。
在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] |
------------- ---------------------------------------------------------
双指针到双指针A指向一个存储块的第一个元素A [0],该元素的元素本身就是双指针。您可以将这些双指针想象成矩阵的行。这就是每个双指针为double类型的num_cols元素分配内存的原因。此外,A [i]指向第i行,即A [i]指向A [i] [0],而这仅是第i行的存储块的第一个双元素。最后,您可以使用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;
}
今天,我在为工作进行编程时使用了双指针,因此我可以回答为什么我们必须使用双指针(这是我实际上第一次使用双指针)。我们必须处理包含在某些结构成员中的缓冲区中的帧的实时编码。在编码器中,我们必须使用指向这些结构之一的指针。问题是我们的指针被更改为指向另一个线程的其他结构。为了在编码器中使用当前结构,我必须使用双指针,以指向在另一个线程中修改的指针。起初并不明显,至少对我们而言,我们必须采用这种方法。在此过程中打印了很多地址:))。
当您处理在应用程序其他位置更改的指针时,应使用双指针。当您处理返回并寻址到您的硬件时,您可能还会发现必须使用双指针。
比较变量的修改值与指针的修改值:
#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;
}
这有助于我避免在被调用函数(用于单链接列表)修改了指针时返回指针的值。
OLD(坏):
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);
...
}
double*
。