• C语言常考面试基础问题


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

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

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

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

    局部变量

    实例:

    #include 
    
    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; 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    全局变量

    实例:

    #include 
    //全局变量声明
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

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

    #include 
     
    /* 全局变量声明 */
    int g = 20;
     
    int main ()
    {
      /* 局部变量声明 */
      int g = 10;
     
      printf ("value of g = %d\n",  g);// g的值是10
     
      return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    形式参数

    实例:

    #include 
     
    /* 全局变量声明 */
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    输出为:

    value of a in main() = 10
    value of a in sum() = 10
    value of b in sum() = 20
    value of c in main() = 30
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

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

    参考: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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    如上结构体所示,由于规则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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    规则2:

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

    struct test_003
    {
    	double a; //8
    	int b;    //4 + 4*[]
    };// 8 + 8  = 16 byte
    
    • 1
    • 2
    • 3
    • 4
    • 5

    该结构体的大小为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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    规则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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    再看一个实例:

    #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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    再看一个实例:

    #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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    注意:

    注意:如果这个规定的对齐数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
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    C共用体

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

    定义:

    union [union tag]
    {
       member definition;
       member definition;
       ...
       member definition;
    } [one or more union variables];
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    实例:

    #include 
    #include 
    
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    输出为:

    size of data is 20.
    data.a is 1917853763
    data.b is 4122360580327794900000000000000.000000
    data.str is C Programming
    Hello World!
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

    #include 
    #include 
    
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    输出:

    size of data is 20.
    data.a is 100
    data.b is 3.141593
    data.str is C Programming
    Hello World!
    
    • 1
    • 2
    • 3
    • 4
    • 5

    静态变量

    static

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

    实例:

    #include 
    
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    输出:

    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!
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

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

    const

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

    实例:

    #include 
    
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    输出:

    value of num is 10.
    Hello world!
    
    • 1
    • 2

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

  • 相关阅读:
    华为OD机试真题-勾股数元组-2023年OD统一考试(B卷)
    python正则表达式
    9、学习MySQL DELETE 语句
    Perfetto分析进阶
    【C++】简化源码——vector的模拟实现
    外包干了三年,真废了。。。
    【MySQL】mysql | 数据处理 | 行转列 | 一种行转列的处理思路
    Spring源码解析-注册配置信息
    Docker存储卷
    JWT的原理及实际使用
  • 原文地址:https://blog.csdn.net/qq_40700822/article/details/126437094