C语言常考面试基础问题

参考:菜鸟教程:https://www.runoob.com/

局部变量和全局变量(C 作用域规则)

  • 任何一种编程中,作用域是程序中定义的变量所存在的区域,超过该区域变量就不能被访问。C 语言中有三个地方可以声明变量:
  • 函数或块内部的局部变量
  • 在所有函数外部全局变量
  • 形式参数的函数参数定义中

让我们来看看什么是局部变量、全局变量和形式参数。

局部变量

实例:

#include <stdio.h>

int main(int argc, char const *argv[])
{
	//局部变量声明
	int a,b,c;

	//局部变量的初始化
	a = 10;
	b = 20;
	c = a + b;

	printf("c is %d. \n",c);
	printf("Hello World!\n");
	return 0; 
}

全局变量

实例:

#include <stdio.h>
//全局变量声明
int c;

int main(int argc, char const *argv[])
{
	//局部变量声明
	int a,b;

	//局部变量的初始化
	a = 10;
	b = 20;

	//全局变量初始化
	c = a + b;

	printf("c is %d. \n",c);
	printf("Hello World!\n");
	return 0;
}

在程序中,局部变量全局变量的名称可以相同,但是在函数内,如果两个名字相同,会使用局部变量值,全局变量不会被使用。下面是一个实例:

#include <stdio.h>
 
/* 全局变量声明 */
int g = 20;
 
int main ()
{
  /* 局部变量声明 */
  int g = 10;
 
  printf ("value of g = %d\n",  g);// g的值是10
 
  return 0;
}

形式参数

实例:

#include <stdio.h>
 
/* 全局变量声明 */
int a = 20;
 
int main ()
{
  /* 在主函数中的局部变量声明 */
  int a = 10;
  int b = 20;
  int c = 0;
  int sum(int, int);
 
  printf ("value of a in main() = %d\n",  a);
  c = sum( a, b);
  printf ("value of c in main() = %d\n",  c);
 
  return 0;
}
 
/* 添加两个整数的函数 */
int sum(int a, int b)
{
    printf ("value of a in sum() = %d\n",  a);
    printf ("value of b in sum() = %d\n",  b);
 
    return a + b;
}

输出为:

value of a in main() = 10
value of a in sum() = 10
value of b in sum() = 20
value of c in main() = 30

结构体内存对齐原则(重点)

参考:https://www.bilibili.com/video/BV1pq4y1c7kP?p=3&spm_id_from=333.880.my_history.page.click&vd_source=2084dd81b188cc57a436867276eec839

  • 结构体对齐规则

  • 1、规则1:
    结构体的成员变量的起始地址(偏移量),是该成员变量的数据类型的大小(字节byte)的整数倍。
    如int是4个字节,那就只能存放在偏移量为0,4,8,…开始的内存地址。结构体的第一个变量的偏移量为0。
    或者说,结构体成员的内部偏移量(内部地址)要被这个成员的数据类型的大小整除。 即 偏移量/sizeof(数据类型)。

  • 2、规则2:
    整个结构体的大小,必须是其内部的最大成员变量的数据类型的大小的整数倍。
    如最大成员变量为double类型,那么整个结构体的大小必须是8的整数倍。

  • 3、规则3:
    对于结构体内部嵌套的结构体,一般按照结构体展开之后的内存对齐来处理。

  • 4、规则4:
    人为指定特定的对齐规则!
    使用#pragma pack(n) 注意:n必须为1或者2的整数倍。
    即 地址起始的偏移量为n的整数倍,结构体的大小也为n的整数倍。
    指定每个成员的起始地址,按照n来对齐,覆盖规则1和规则2。

  • 5、 注意
    如果这个规定的对齐数n比结构体内部的所有的成员变量的数据类型都要大,则就取两者中的小的。 是对第一条规则的补充。

规则1:

规则1: 结构体的成员变量的起始地址(偏移量),是该成员变量的数据类型的大小(字节byte)的整数倍。
如int是4个字节,那就只能存放在偏移量为0,4,8,…开始的内存地址。结构体的第一个变量的偏移量为0。
或者说,结构体成员的内部偏移量(内部地址)要被这个成员的数据类型的大小整除。 即 偏移量/sizeof(数据类型)。
实例:

// [] 代表一个字节byte。
struct test_001
{
	char c1; //1 + 3*[]
	int  c2; //4
	char c3; //1 + 3*[]
};// 4 + 4 + 4 = 12 byte

如上结构体所示,由于规则1的对齐原则,int类型的起始地址的偏移量为4,再由于规则2结构体的大小规则,即该结构体的大小为最大成员变量的数据类型的大小的整数倍,即int(4 byte)的整数倍,故最终大小为12 byte

再看一个实例:

struct test_002
{
	char c1; //1 
	char c3; //1 + 2*[] 
	int  c2; //4
};// 1 + 3 + 4 = 8 byte

规则2:

规则2: 整个结构体的大小,必须是其内部的最大成员变量的数据类型的大小的整数倍。
实例:

struct test_003
{
	double a; //8
	int b;    //4 + 4*[]
};// 8 + 8  = 16 byte

该结构体的大小为double (8 byte)的整数倍,故最终大小为16 byte

规则3:

规则3:对于结构体内部嵌套的结构体,一般按照结构体展开之后的内存对齐来处理。
实例:

struct test_003
{
	double a; //8
	int b;    //4 + 4*[]
};// 8 + 8  = 16 byte

struct test_004
{
	char c1; //1 + 3*[]
	int  c2; //4
	char c3; //由于下面的结构体的第一个成员变量为double类型,根据规则1,所以大小为 1 + 7*[]

	struct test_003 test3; // 8 + 8 = 16 byte
};// 4 + 4 + 8 + 16 = 32 byte

规则4:

规则4: 人为指定特定的对齐规则! 使用#pragma pack(n) 注意n必须为1或者2的整数倍。 指定每个成员的起始地址,按照n来对齐,覆盖规则1和规则2。

实例:

#pragma pack(1) // 规定偏移量为1.即 地址起始的偏移量为1的整数倍。结构体的大小也为1的整数倍。

struct test_003
{
	double a; //8
	int b;    //4 
};// 8 + 8  = 12 byte

struct test_004
{
	char c1; //1 
	int  c2; //4
	char c3; //1 

	struct test_003 test3; // 8 + 4 = 12 byte
};// 1 + 4 + 1 + 12 = 18 byte

再看一个实例:

#pragma pack(2)  // 规定偏移量为2.即 地址起始的偏移量为2的整数倍。结构体的大小也为2的整数倍。

struct test_003
{
	double a; //8 
	int b;    //4
};// 8 + 4  = 2 byte

struct test_004
{
	char c1; //1 + 1*[]
	int  c2; //4
	char c3; //1 + 1*[]

	struct test_003 test3; // 8 + 4 = 12 byte
};// 2 + 4 + 2 + 12 = 20 byte

再看一个实例:

#pragma pack(4) // 规定偏移量为4. 即 地址起始的偏移量为4的整数倍。结构体的大小也为4的整数倍。


struct test_003
{
	double a; //8
	int b;    //4
};// 8 + 4  = 12 byte

struct test_004
{
	char c1; //1 + 3*[]
	int  c2; //4
	char c3; //1 + 3*[]
	
	struct test_003 test3; // 8 + 4 = 12 byte
};// 4 + 4 + 4 + 12 = 24 byte

注意:

注意:如果这个规定的对齐数n比结构体内部的所有的成员变量的数据类型都要大,则就取两者中的小的。 是对第一条规则的补充。

#pragma pack(16) // 规定偏移量为16.  由于16大于8,所以该偏移量无效,相当于8。

struct test_003
{
	double a; //8
	int b;    //4 + 4*[]
};// 8 + 8  = 16 byte

struct test_004
{
	char c1; //1 + 3*[]
	int  c2; //4
	char c3; //由于下面的结构体的第一个成员变量为double类型,根据规则1,所以大小为 1 + 7*[]

	struct test_003 test3; // 8 + 8 = 16 byte
};// 4 + 4 + 8 + 16 = 32 byte

C共用体

  • 共用体是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型。您可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式。

定义:

union [union tag]
{
   member definition;
   member definition;
   ...
   member definition;
} [one or more union variables];

实例:

#include <stdio.h>
#include <string.h>

union Data
{
	int a;
	float b;
	char str[20];

};

int main(int argc, char const *argv[])
{
	union Data data;

	data.a = 100;
	data.b = 3.1415;
	strcpy(data.str, "C Programming") ;

	int size = sizeof(data);

	printf("size of data is %d.\n",size);

	printf("data.a is %d\n", data.a);
	printf("data.b is %f\n", data.b);
	printf("data.str is %s\n", data.str);

	printf("Hello World!\n");
	return 0;
}

输出为:

size of data is 20.
data.a is 1917853763
data.b is 4122360580327794900000000000000.000000
data.str is C Programming
Hello World!

可见,共用体内,只能同时使用一个成员变量,且共用体的大小为最大成员变量的大小。

#include <stdio.h>
#include <string.h>

union Data
{
	int a;
	float b;
	char str[20];

};


int main(int argc, char const *argv[])
{
	union Data data;

	int size = sizeof(data);

	printf("size of data is %d.\n",size);

	data.a = 100;
	printf("data.a is %d\n", data.a);

	data.b = 3.1415926;
	printf("data.b is %f\n", data.b);

	strcpy(data.str, "C Programming");
	printf("data.str is %s\n", data.str);

	printf("Hello World!\n");
	return 0;
}

输出:

size of data is 20.
data.a is 100
data.b is 3.141593
data.str is C Programming
Hello World!

静态变量

static

  • (1)在修饰变量的时候,static 修饰的静态局部变量只执行初始化一次,而且延长了局部变量的生命周期,直到程序运行结束以后才释放。
  • (2)static 修饰全局变量的时候,这个全局变量只能在本文件中访问,不能在其它文件中访问,即便是
    extern外部声明也不可以。
  • (3)static 修饰一个函数,则这个函数的只能在本文件中调用,不能被其他文件调用。static 修饰的变量存放在全局数据区的静态变量区,包括全局静态变量和局部静态变量,都在全局数据区分配内存。初始化的时候自动初始化为 0
  • (4)不想被释放的时候,可以使用static修饰。比如修饰函数中存放在栈空间的数组。如果不想让这个数组在函数调用结束释放可以使用 static 修饰。
  • (5)考虑到数据安全性(当程序想要使用全局变量的时候应该先考虑使用 static)。

实例:

#include <stdio.h>

int main(int argc, char const *argv[])
{

	
	for (int i = 0; i < 10; ++i)
	{
		static int a = 10;
		a = a + 1;
		printf("value of a is %d.\n", a);
		
	}
	printf("Hello world!\n");
	return 0;
}

输出:

value of a is 11.
value of a is 12.
value of a is 13.
value of a is 14.
value of a is 15.
value of a is 16.
value of a is 17.
value of a is 18.
value of a is 19.
value of a is 20.
Hello world!

static声明的变量只有一次有效的初始化,并且其内存上的值是可以被修改的,直到程序重新运行后,在重新赋值。

const

  • const 声明一个只读变量,声明之后不允许改变。意味着,一旦声明必须初始化,否则会报错。

实例:

#include <stdio.h>

const int num = 10;

int main(int argc, char const *argv[])
{
	printf("value of num is %d.\n", num);
	printf("Hello world!\n");
	return 0;
}

输出:

value of num is 10.
Hello world!

如果要修改num的值,则会报错。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

iNBC

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值