7-163 谷歌的招聘
由一道编程题引发的C中关于数组、指针的思考
先来看三种数组定义方式
#include <stdio.h>
#include <stdlib.h>
int main(){
//方式1
int array_1[4] = {1};
//方式2,变长数组
int n2;
scanf("%d",&n2);
int array_2[n2]; //使用变长数组
printf("%d\n",sizeof (arr ay_2) / 4 );
//方式3,使用malloc函数动态分配内存
int m=4;
int *array_3 = (int*)malloc(m * sizeof(int));
printf("%d",sizeof (array_3) / 4);
}
这三种都是在C语言中定义数组的方法,但它们在数组大小决定方式、内存分配位置和生命周期等方面有所不同。
- 固定大小的数组
这是最基本的数组定义方式。在这种方式中,数组的大小在编译时就已经确定了。
这种数组在栈上分配内存,生命周期与其所在的函数相同。当函数返回时,这种数组会自动被销毁。 - 变长数组
在这种方式中,数组的大小是在运行时决定的。这种类型的数组在C99标准中被引入。
这种数组在栈(Stack)上分配内存,而栈内存的大小通常比堆内存(Heap)小得多。当函数返回时,这种数组会自动被销毁,也就是说,它的生命周期与其所在的函数相同。
注意,虽然变长数组在C99中被引入,但在C11标准中,它们被转移到了可选功能中(optional feature)。并且,一些编译器(例如GCC)默认允许变长数组,但一些其他编译器(例如Clang)则不允许。因此,使用变长数组的可移植性可能会有问题。 - 使用malloc函数动态分配内存
在这种方式中,数组的大小也是在运行时决定的。但这种数组在堆(Heap)上分配内存,堆内存通常比栈内存大得多。
这种数组的生命周期直到你显式地调用free函数来释放它为止。这意味着,即使函数返回,这种数组也不会被自动销毁,除非你显式地销毁它。
使用malloc函数动态分配内存的一个主要优点是,可以在运行时分配大量的内存。但是,需要记住在不再需要数组时释放它,否则可能会导致内存泄漏。
内存泄漏:内存泄漏是一种常见的编程问题,特别是在手动内存管理的编程语言(如C或C++)中。它发生在当程序持续地分配内存但未能相应地释放它时。未被释放的内存块随着时间的推移会累积起来,导致可用内存逐渐减少,这可能会降低程序的性能,甚至导致程序崩溃。
更具体地说,当你在C语言中使用malloc、calloc或realloc函数分配内存时,操作系统会为你的程序分配一块内存。这块内存会一直保留,直到你显式地调用free函数来释放它。如果你忘记了释放这块内存,或者由于错误的程序逻辑而未能释放它,那么这块内存就会“泄漏”,它将无法被程序的其他部分使用,即使程序可能不再需要它。
上述代码存在错误
在第三种方法中。试图通过sizeof (array_3) / 4来获取动态分配数组的长度。
但是,这并不会给出正确的结果,因为array_3是一个指针,sizeof (array_3)将返回指针的大小,而不是它指向的内存块的大小。在大多数系统上,一个指针的大小是8字节(对于64位系统),所以sizeof (array_3) / 4的结果将是2,而不是期望的数组长度。要获取动态分配的数组长度,需要自己保存这个信息。
另外的问题:
第二种方式的array_2不也是一个指针吗,为什么方式二可以正确获取数组大小?
在C语言中,数组名确实可以被看作是一个指向数组首元素的指针。但是,静态数组(包括固定大小的数组和变长数组)和动态数组(由
malloc
等函数分配的数组)在这方面有一些关键的不同。
对于静态数组(包括方式1和方式2的数组),虽然数组名可以被当作一个指针来使用,但它实际上并不是一个真正的指针。数组名更像是一个指向数组首元素的常量指针,并且编译器知道这个数组的大小。因此,当你在静态数组上使用sizeof
运算符时,你会得到整个数组的大小,而不是指针的大小。
但是,对于动态数组(方式3的数组),malloc
返回的是一个真正的指针。这个指针指向分配的内存块的首地址,但编译器并不知道这个内存块的大小。因此,在动态数组上使用sizeof
运算符时,只会得到指针的大小,而不是整个数组的大小。
这就是为什么可以通过sizeof(array_2) / sizeof(int)
来获取方式2的数组的长度,但不能通过sizeof(array_3) / sizeof(int)
来获取方式3的数组的长度的原因。在动态分配内存时,需要自己保存数组的长度信息。
#include <stdio.h>
#include <stdbool.h>
#include <math.h>
bool isPrime(int n){
if(n == 1) return false;
for(int i=2;i < n; i++){
if(n % i == 0){
return false;
}
}
return true;
}
int main(){
int n,k,tmp,m=0;
int num[1000];
scanf("%d %d",&n, &k);
//把数据存到数组
for(int i=0; i<n; i++){
scanf("%1d",&tmp);
num[i] = tmp;
}
//开始从第i位寻找连续k位素数
for(int i=0; i<=n-k; i++){
int account=k-1;
for(int j=i; j<i+k; j++){ //循环k次,计算数字
m += num[j] * (int)pow(10,account);
account--;
}
if(isPrime(m)){
printf("%0*d", k, m); //*作为宽度指定符
return 0;
}
m=0; //准备重新寻找
}
printf("404");
return 0;
}
- printf中,
*
作为宽度指定符