[linux小水滴]C语言基础

1 static变量的作用

static修饰变量的作用,如下实例,函数fun()中的count值并不会受到main()中的count值的影响。等于说fun()函数中的count变量对函数外部是不可见的,而且其值会继承上一次的返回值。静态全局变量存储在静态存储区,所以它具备持久性和默认值01

//实例
#include <stdio.h>

int fun(void){
    static int count = 10;    // 事实上此赋值语句从来没有执行过
    return count--;
}

int count = 1;

int main(void)
{    
    printf("global\t\tlocal static\n");
    for(; count <= 10; ++count)
        printf("%d\t\t%d\n", count, fun());    
   
    return 0;
}
/*
程序的运行结果是:
global          local static
1               10
2               9
3               8
4               7
5               6
6               5
7               4
8               3
9               2
10              1

补充:static类型的函数,作用域并不在整个源程序,而仅仅作用于该函数所定义的那个源文件中。

2 结构体变量的初始化

结构体变量中的成员变量前带点,是对成员变量的强制初始化。如下:

//实例:
#include <stdio.h>
struct student{
    int year;
    int ID; 
};
 
int main(void)
{
 
    struct student s3 ={
     .year = 2019, 
     .ID = 3333};    

    printf("s3.ID = %d \n",s3.ID);
    printf("s3.year = %d \n\n",s3.year);
    
 }
/* 
运行结果:
s3.ID = 3333 
s3.year = 2019

3 return; 这是啥意思

return;//函数中return后面直接加分号,表示直接返回(void),结束函数。

4 指针中常见的&和*

&取址符号(取旧地址的动作),p指针变量(指向一个新地址),*p指针变量p所指的地址中的内容,实例:

int i = 3;
int *p;
p = &i;
*p = 3

5 C语言符号运算顺序

 if (type && strlen(type) >= NAME_LENGTH)
	return ERR_PTR(-EINVAL);

这里注意,符号的运算顺序,&&优先级低于>=。所以这里先比较strlen(type)和NAME_LENGTH的大小,然后和type与。
附:各种运算符的优先级:! > 算术运算符 > 关系运算符 > && > || > 赋值运算符

6 continue的作用?

continue语句的作用是跳过循环体中剩余的语句而强制进入下一次循环。continue语句只用在 while、for 循环中,常与 if 条件语句一起使用,判断条件是否成立。

#include <stdio.h>
//下面这段代码的功能:输入一段数字,输出时跳过4 5,可以看到continue存在的意义。
int main(){
    char c = 0;
    while(c!='\n'){  //回车键结束循环
        c=getchar();
        if(c=='4' || c=='5'){  //按下的是数字键4或5
            continue;  //跳过当次循环,进入下次循环
        }
        putchar(c);
    }
    return 0;
}
运行结果:
012345678901236789

7 linux宏定义中的(~0)表示什么?

表示对0逐位求反,全部变成1,1的位数由机器决定。
举例:(~0)<<5 如果0是一个u8型的变量,则得值0b11100000

//linux常见宏定义中的(~0)到底指什么?
#include <stdio.h>
int main()
{
  printf("%x", (~0));
  printf("%d", (~0));	
}
/*
运行结果:
ffffffff
-1

8 UL和0UL

1UL //无符号长整型1
0UL //无符号长整型0

9 snprintf()

格式化字符串,并返回字符串长度。

int snprintf ( char * str, size_t size, const char * format, ... );

入口参数:
str – 目标字符串。
size – 拷贝字节数(Bytes)。
format – 格式化成字符串。
… – 可变参数。
返回值:字符串长度
举例:

#include <stdio.h>
int main()
{
    char buffer[50];
    char* s = "testcase";
 
    // 读取字符串并存储在 buffer 中
    int j = snprintf(buffer, 5, "%s\n", s);
 
    // 输出 buffer及字符数
    printf("string:\n%s\ncharacter count = %d\n", buffer, j);
 
    return 0;
}
运行结果:
string:
test
character count = 9
这里的size为什么设为5输出的字符串不是“testc”?因为size为5的五个字节包含"test\0"默认以"\0"结尾。
注意返回的是整个被格式化的字符串的长度。

10 "?:"运算符注意事项

最常见的用法如下:

A ? A : B //A为真,返回值为A;A为假,返回值为B
A ?: B //同上,缩写

我们常常用这个运算符来代替if else语句来简化代码。
注意:A和B可以是变量也可以是运算公式或逻辑对比关系,比如:

#include <stdio.h>

int main()
{
   int a=7, b=6;
   int c;
   c = a > b ?: b;//相当于(a>b) ? (a>b) : b
   printf("a = %d, b = %d, c = %d\n", a, b, c);
   
   return 0;
}

//运行结果
a = 7, b = 6, c = 1

11 #ifdef和#if defined的区别

两者含义相同,都可以用#endif来结束一个条件的条件编译,区别在于:#if defined可以有两个以上的条件,#ifdef只能有“真”和“非真”两个条件,具体用法如下所示:

#ifdef CONDITION
//......
#else
//......
#endif
#if defined CONDITION_A
//......
#elif defined CONDITION_B
//......
#else
//......
#endif

12 #include <xxx.h>和"xxx.h"的区别

#include <xxx.h> 编译时调用标准头文件(即标准库中的头文件)
#include"xxx.h" 编译时调用自定义头文件,当然,也可以调用标准头文件。但是为了区分出自定义头文件,还是各用各的比较好。

13 头文件中的#ifndef #define #endif?

我们常常会看到头文件以下面的格式编写:

#ifndef _XXX_H
#define _XXX_H
//.....
//头文件中的内容
//....
#endif

那么,在头文件中加这三句的意义是什么呢?
理解:如果没有定义_XXX_H(#ifndef),那么定义_XXX_H(#define),最后结束(#endif)。
在源文件调头文件的时候首先检查这个头文件是否被定义过,这样可以 避免头文件的重定义,防止重复定义宏和重复包含头文件,提高编译效率。

14 数组指针和指针数组

数组指针和指针数组是容易混淆的两个概念,这里先简单介绍下区别,有时间再补充。
指针数组,它肯定是个数组,下面是一个常见的指针数组。这个数组有三个元素,每个元素都是一个char *类型的指针变量(这个指针指向字符串的首地址)

char *parr[3] = {"lost", "in", "paradise"};

数组指针,首先它是一个指针变量,注意是一个。这个指针指向一个数组。比如下面的这个数组指针可以理解为(*arrp)----指向—>char[3],即指向一个有三个char类型元素的数组

char (*arrp)[3];

15 共用体union

union和struct有点类似,都是一种数据结构类型,它们都可以有不同的成员变量包含在其中。但它们的内存大小分配是不同的,union变量的内存大小仅满足所需存储空间最大的成员变量,即存储空间所有成员共用,等于说占用的是同一段内存;而struct变量分配足够的空间给所有成员,每个成员都可以存下,各个成员互不影响。
1.union变量的两种定义方法,和struct类似:
(1)直接定义union类型的变量(推荐)

/* union数据结构在kernel中的应用示例(kernel5.4) */
typedef struct spinlock {
	union {
		struct raw_spinlock rlock;

#ifdef CONFIG_DEBUG_LOCK_ALLOC
# define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map))
		struct {
			u8 __padding[LOCK_PADSIZE];
			struct lockdep_map dep_map;
		};
#endif
	};
} spinlock_t;

(2)先定义union类型,再定义union类型的变量

/* 类型定义 */
union class
{
  char name[10];
  int num;
};
/* 变量定义 */
union class class1, class2, *class3;

2.简单举例

#include <stdio.h>
#include <string.h>
union class {
        int num; /* int类型占用4个字节 */
        char name[20]; /* char类型1个字节,name数组共占用10个字节 */
} cl;

int main() {
        cl.num = 30;
        /* 当cl.name被赋值时,cl.num所占的内存将被cl.name占用 */
        strcpy(cl.name,"math_class");

        printf("班级人数 = %d, %p\n", cl.num, &cl.num); /* 打印变量值和存储变量内存的起始地址 */
        printf("班级名称 = %s, %p\n", cl.name, &cl.name); /* 起始地址同上 */

        cl.num = 30;/* 内存重新覆盖新值 */
        printf("班级人数 = %d\n", cl.num);

        strcpy(cl.name,"music_class");
        printf("班级名称 = %s", cl.name);

        return 0;
}
运行结果:
班级人数 = 1752457581, 0x404040
班级名称 = math_class, 0x404040
班级人数 = 30
班级名称 = music_class

3.特别注意union成员的对齐,下面的示例可以看出其对齐规则

#include <stdio.h>
union class {
        long num;
        char name[20];
} cl;

union class_2 {
        int num;
        char name[11];
} cl_2;

int main() {
        printf("cl.num = %ld \n", sizeof(cl.num));
        printf("cl.name = %ld \n", sizeof(cl.name));
        printf("union_cl = %ld \n", sizeof(cl)); /* 按long int的8个字节的整数倍对齐 */
        printf("union_cl_2 = %ld \n", sizeof(cl_2)); /* 按int的4个字节的整数倍对齐 */
        return 0;
}
运行结果:
cl.num = 8 
cl.name = 20 
union_cl = 24 
union_cl_2 = 12 

4.共用体有什么实际的意义?
本质上是在内存划了一个可重复利用的空间,内存使用更为精细灵活,也节省了内存空间。
实际应用,比如用于大小端的判断:

#include<stdio.h>
union var{
        char c[4];
        int i;
};
int main(){
        union var data;
        data.c[0] = 0x04;
        data.c[1] = 0x03;
        data.c[2] = 0x02;
        data.c[3] = 0x11;
        printf("%x\n",data.i);/* 如果i输出的值高位存的是标号小的,则为大端,如果低位存的是标号小的,则为小端 */
}
运行结果:
11020304
说明是小端

参考链接:
基本概念
对齐规则
使用方法及其本质

16 do{…}while(0)的作用?

内核示例:

// kernel5.4/include/linux/spinlock.h
#define spin_lock_nest_lock(lock, nest_lock)				\
do {									\
	raw_spin_lock_nest_lock(spinlock_check(lock), nest_lock);	\
} while (0)

内核代码中经常能看到do{}while(0),今天在spin_lock_irqsave()的函数宏定义中看到了这个do{…}while(0),就想了解下这个看起来没有意义的while(0)到底起什么作用?
作用一、避免引用时出错
因为在引用宏定义的时候,是单纯字符串的替换,对于行数比较多的复杂宏定义,特别是函数宏定义在引用时很容易出错。如下情况:

#define FUN()\
	fun0();\
	fun1()

如果调用时只调用FUN();,那么正常地替换成fun0();\ fun1();;如果在执行FUN()前有约束条件,如下所示,会失去原本的语义:

if (a)
	FUN();

||
||
v

if (a)
	fun0();
fun1();

如果函数宏定义中没有do while(0),我想养成给条件语句的执行语句加括号的习惯,也可以避免这种引用失误。即if(a) { FUN(); }
作用二、代替goto语句
如下例:

do  
{  
	if (条件)  
	{  
		执行语句1;
		break;  
	}   
	执行语句2;
	执行语句3} while(0)

作用三、避免空宏引起的warning
作用四、定义一个单独的函数块来实现复杂的操作
详细参考链接:
http://www.spongeliu.com/415.html
https://www.cnblogs.com/lanxuezaipiao/p/3535626.html

17 代码段中的goto标签没有被执行,最后还会被执行吗?

疑问引出点,为如下代码段加锁tz->lock,如果if语句执行了,那么goto exit,直接释放锁跳出函数。
如果if语句不执行呢?不执行就不能goto exit,是不是就意味着锁不会被释放了?
因此,代码段中的goto的标签没有被执行,最后还会被执行吗?

//kernel5.4/drivers/thermal/thermal_helpers.c
void thermal_zone_set_trips(struct thermal_zone_device *tz)
{
	//.....
	mutex_lock(&tz->lock);

	if (!tz->ops->set_trips || !tz->ops->get_trip_hyst)
		goto exit;
	//........
	ret = tz->ops->set_trips(tz, low, high);
	if (ret)
		dev_err(&tz->device, "Failed to set trips: %d\n", ret);

exit:
	mutex_unlock(&tz->lock);
}

答案是无论如何都会被执行,除非在goto之前直接return了。简单举例:

#include <stdio.h>

int main()
{
	int a = 1;
	if(a)
		goto enter;
	
enter:
	printf("a=%d\n", a);
exit:
	printf("exit!\n");
}
运行结果:
a=1
exit!
exit标签并没有被goto语句调到,但最后还是被执行了!

  1. static变量 ↩︎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值