我正在学习C,因为我被困在指针章节。指向指针(** p)部分的指针,用于处理2维数组。
以下函数生成一个2维数组,其所有元素都等于0。
double** example(int rows, int cols){
static double tr[rows][cols];
for(int i = 0; i < rows; i++)
for(int j = 0; j < cols; j++)
tr[j][i] = 0;
return tr;
}
int main(void){
double **tr;
int m = 2 ;
int n = 2 ;
tr = transpose(m, n);
return 0;}
函数返回的tr
是tr
数组的第一个元素的“双指针”吗?或者它的价值?此外,现在(在调用函数之后)如何访问** tr指向的内存部分的数组元素?
有人可以解释一下这一切是如何运作的吗?或者向我推荐一本书中的文章或章节?
答案 0 :(得分:4)
在C中,函数必须返回与函数相同类型的值/变量。在这个例子中,tr将是一个双指针,指向第一行。
当使用双指针创建2D数组时,实际上是在创建一个指向指针数组的指针,数组中的指针指向存储在每一行中的字符串/值。双指针将指向指针数组中的第一个指针,指针将指向第一行中的第一个元素。
您可以使用括号表示法访问2D数组中的值,例如
tr[row][column]
或*(*(tr + row) + column)
。对于第二种表示法,*(tr + row)
将访问指向所需行的指针,然后您可以使用*(found_row + column)
引用该值,从而为您提供所需列中的元素。
答案 1 :(得分:2)
提供的程序完全不正确。可变长度数组可能没有静态存储持续时间/根据C标准(6.7.6.2数组声明符)
2如果标识符被声明为具有可变修改类型,则为 应为普通标识符(如6.2.3中所定义),没有联系, 并具有块范围或功能原型范围。 如果是 标识符被声明为具有静态或线程存储的对象 持续时间,它不应具有可变长度数组类型。
此外,指针类型double **
和double ( * )[cols]
(数组提供正确声明的指针在表达式中转换)不兼容。所以功能错了。
double** example(int rows, int cols){
static double tr[rows][cols];
for(int i = 0; i < rows; i++)
for(int j = 0; j < cols; j++)
tr[j][i] = 0;
return tr;
}
这是一个演示程序,展示了如何处理可变长度数组。
#include <stdio.h>
void init( size_t rows, size_t cols, double a[rows][cols] )
// or
// void init( size_t rows, size_t cols, double a[][cols] )
// or
// void init( size_t rows, size_t cols, double ( *a )[cols] )
{
for ( size_t i = 0; i < rows; i++ )
{
for ( size_t j = 0; j < cols; j++ ) a[i][j] = 0.0;
}
}
void display( size_t rows, size_t cols, double a[rows][cols] )
// or
// void display( size_t rows, size_t cols, double a[][cols] )
// or
// void display( size_t rows, size_t cols, double ( *a )[cols] )
{
for ( size_t i = 0; i < rows; i++ )
{
for ( size_t j = 0; j < cols; j++ ) printf( "%lf", a[i][j] );
putchar( '\n' );
}
}
int main(void)
{
while ( 1 )
{
size_t m, n;
printf( "Enter numbers of rows and columns (0 - exit): " );
if ( scanf( "%zu%zu", &m, &n ) != 2 || m == 0 || n == 0 ) break;
double a[m][n];
putchar( '\n' );
init( m, n, a );
display( m, n, a );
putchar( '\n' );
}
return 0;
}
它的输出可能看起来像
Enter numbers of rows and columns (0 - exit): 2 3
0.0000000.0000000.000000
0.0000000.0000000.000000
Enter numbers of rows and columns (0 - exit): 3 4
0.0000000.0000000.0000000.000000
0.0000000.0000000.0000000.000000
0.0000000.0000000.0000000.000000
Enter numbers of rows and columns (0 - exit): 0 0
在这两个函数中,第三个参数被调整为指针类型double ( *a )[cols]
。它与double **a
不同。
如果您要编写以下程序
#include <stdio.h>
#define M 2
#define N 3
int main(void)
{
int a[M][N] =
{
{ 1, 2, 3 },
{ 4, 5, 6 }
};
int **p = ( int ** )a;
p[0][0] = 10;
return 0;
}
然后它会有未定义的行为,因为p[0]
会考虑元素a[0][0]
中存储的值(或元素a[0][0]
和a[0][1]
中存储的组合值,具体取决于大小指针的值为1作为指针值,并尝试访问表达式p[0][0]
中地址1的内存。
答案 2 :(得分:0)
丢掉包含此代码的书 - 它甚至不能编译。我收到以下错误:
[fbgo448@n9dvap997]~/prototypes/buf: gcc -o stack -std=c99 -pedantic-errors -Wall stack.c
stack.c: In function âexampleâ:
stack.c:2: error: storage size of âtrâ isnât constant
stack.c:6: error: return from incompatible pointer type
您无法创建具有static
存储持续时间的可变长度数组。具有static
存储持续时间的对象在程序启动时分配 ,因此需要在编译时知道它们的大小。在运行时知道其维度的大小之前,无法实例化VLA。
除非它是sizeof
或一元&
运算符的操作数,或者是用于在声明中初始化字符数组的字符串文字,表达式为类型&#34; T
&#34;的N元素数组;将被转换(&#34;衰减&#34;)到类型为&#34的表达式;指向T
&#34;的指针,并且表达式的值将是第一个元素的地址。阵列。
表达式 tr
的类型为&#34; rows
- cols
的元素数组 - double
的元素数组&#34; ;在return
语句中,此表达式将“衰减”#34;输入&#34;指向cols
的指针 - double
&#34;或double (*)[cols]
的元素数组。这是来自double **
的完全不同的类型。
多个间接显示了很多,但这不是一个有效的例子。
当您希望函数写入指针类型的参数时,通常会看到多个间接:
void bar( T **p ) // for any type T
{
*p = new_pointer_value(); // write a new value to the thing p points to
}
void foo( void )
{
T *ptr;
bar( &ptr ); // bar writes a value to ptr
}
您可以使用多个间接来创建一个有点排序的结构,其行为类似于2D数组,但它与2D数组不同:
double **arr;
size_t rows = some_number_of_rows();
size_t cols = sime_number_of_cols();
arr = malloc( sizeof *arr * rows ); // allocates an array of pointers to double
if ( arr )
{
for ( size_t i = 0; i < rows; i++ )
{
arr[i] = malloc( sizeof *arr[i] * cols ); // allocates an array of double
}
}
当你完成后,你的结构看起来像这样:
+---+ +---+ +---+---+---+ +---+
a: | | ---> | | ---> | | | | ... | |
+---+ +---+ +---+---+---+ +---+
| | -+
+---+ | +---+---+---+ +---+
... +-> | | | | ... | |
+---+ +---+---+---+ +---+
| | -+
+---+ | +---+---+---+ +---+
+-> | | | | ... | |
+---+---+---+ +---+
您可以使用常规下标表示法(a[i][j]
)来访问元素,就像2D数组一样,但是这个结构的内存不像二维数组那样连续分配:
+---+---+---+---+---+
a: | | | | | |
+---+---+---+---+---+
| | | | | |
+---+---+---+---+---+
| | | | | |
+---+---+---+---+---+
...