嵌入式 Linux C语言——C语言基础

嵌入式 Linux C语言——C语言基础

一、数据类型

1、基本数据类型

数据类型是创建变量的模型。变量名是连续存储空间的别名,程序中使用变量命名存储空间,通过变量可以使用存储空间。变量所占的内存大小取决于创建变量的数据类型。

2、有符号和无符号

有符号数中数据类型的最高位用于标识数据的符号,最高位为1表示为负数,最高位为0表示为正数。

计算机中有符号数通常使用补码表示,正数的补码为正数本身,负数的补码为负数的绝对值的各位取反后加1。

计算机中无符号数通常使用原码表示,无符号数默认为正数,没有符号位。对于无符号数来说,MAX_VALUE + 1等于MIN_VALUE,MIN_VALUE - 1等于MAX_VALUE。

无符号数与有符号数进行混合运算时,会将有符号数转换为无符号数后再进行计算,结果为无符号数。

3、浮点数的实现

float与double类型的数据在计算机中的表示方法相同,由于所占存储空间不同,分贝能够表示的数值范围和精度不同。

浮点数在计算机内部存储方式分为三段,符号位、指数、尾数

浮点数的转换需要先将浮点数转换为二进制,将得到的二进制浮点数使用科学计数法表示,根据数据类型计算偏移后的指数。指数的偏移量和数据类型有关,float类型加127,double类型加1023。

8.25的float表示如下:

8.25的二进制表示:1000.01==>1.00001(2^3)

符号位:0

指数:127+3 = 130 = b10000010

小数:00001

8.25的float表示为:0 10000010 00001 000000000000000000 = 0x41040000

由于float和int类型都占四个字节,float能表示的具体数字的个数与int相同,但是float表示的数值时不连续的,不能作为精确数使用,因此float类型的表示范围比int类型表示的范围大。Double类型与float类型在计算机中的表示方法相同,但是double类型占用的存储空间大,因此所能表示的精度更高。

4、类型转换

C语言中的数据类型可以进行转换,包括显示类型转换和隐式类型转换。

强制类型转换时,如果目标类型能够容纳目标值,则转换后结果不变;如果目标类型不能容纳目标值,则结果将产生数据截断。

隐式类型转换时编译器进行的类型转换,低类型到高类型的隐式转换是安全的,结果不会产生数据截断;高类型到低类型的隐式转换是不安全的,结果产生类型截断,结果可能是不正确的。

隐式类型转换发生的时机:

A、算术运算中,低类型转换为高类型

B、赋值表达式中,表达式的值转换为左值的类型

C、函数调用时,实参转换为形参的类型

D、函数返回时,return表达式转换为返回值类型

5、void类型

void修饰函数返回值和参数

void不能用于定义变量,标准C语言编译器中sizeof(void)会报错,GCC编译器中因为进行了扩展,不会报错,结果为1

二、程序结构

1、分支语句

if...else

if语句根据条件选择执行语句,else不能独立存在,但是总是和离它最近的if匹配。

float变量不能直接和0进行比较,需要确定变量在某个较小的区间内。

if(-0.0000001 < a && a < 0.0000001)

switch

switch语句对应单个条件多个值的情况,一般情况下case语句需要有break,否则会导致分支重叠。Default分支需要加上,用于处理特殊情况。

case语句中的值只能是整型或char类型。

2、循环语句

do语句先执行后判断,循环体至少执行一次

do

{

}while();

while语句先判断后执行,循环体可能不执行

while()

{

}

for语句先判断后执行

for(; ; )

break表示终止循环的执行

continue表示中止本次循环,进入下一次循环

三、const和volatile

1、const

const修饰的变量告诉编译期变量是只读的,但还是变量,向编译器指明变量不能做左值

const修饰的局部变量在栈上分配空间

const修饰的全局变量在全局数据区分配空间

const只在编译期有效,在运行期无效

#include <stdio.h>

const int g = 10;

int main(int argc, char **argv)

{

const int c = 1;

int *p = (int *)&c;

*p = 100;

printf("%d\n", c);

p = (int *)&g;

*p = 1000;

printf("%d\n", c);

return 0;

}

编译正常,运行时对使用指针可以对const局部变量进行修改,但使用指针对const全局变量修改时发生段错误。

标准C语言编译器不会将const修饰的全局变量存储于只读存储区,而是存储在可以修改的全局数据区,其值可以通过指针修改。但是现代编译器如GCC将const全局变量存储在只读存储区,通过指针修改其值将发生段错误。

const修饰函数参数表示在函数体内部不希望修改参数的值

const修饰函数返回值表示返回值不可改变

2、volatile

volatile指示编译器不能优化,必须每次到内存中取变量的值,主要修饰被多个线程访问的变量、或是被未知原因更改的变量

const volatile int i = 10;

作为局部变量,i不可以做左值,但可以使用指针修改值,编译器不对变量i进行优化,每次都到内存取值。

四、struct和union

1、struct

柔性数组是数组大小待定的数组

结构体中最后的成员可以是柔性数组,柔性数组只是一个标识符,不占存储空间。

2、union

union只分配最大成员的空间,所有成员共享这个空间。

union的使用会受到计算机大小端的影响

使用union测试计算机大小端的代码如下:

#include <stdio.h>

void big_little(void);

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

{

big_little();

return 0;

}

void big_little(void)

{

union Mode

{

char c;

int i;

};

union Mode m;

m.i = 1;

if(m.c)

{

printf("little\n");

}

else

{

printf("big\n");

}

}

五、enum、sizeof、typedef

1、enum

enum是C语言中的一种自定义类型,enum的值是自定义的整型值,第一个定义的enum值默认为0,默认情况下enum的值是前一个定义的值加1,也可以指定。enum中定义的值是C语言中真正的常量

2、sizeof

sizeof是编译器的内置指示符,用于计算类型或变量所占内存大小,sizeof的值在编译期就已经确定。

int var = 0;

int size = sizeof(var++);

var并不会执行var++,而是在编译时就将sizeof(var++0替换为值4

3、typedef

typedef用于给一个已经存在的类型进行重命名,本质上不产生新类型

typedef重命名的源类型可以在typedef语句后定义,typedef不能使用unsigned、signed修饰。

六、接续符、单引号、双引号

1、接续符

C语言中接续符(\)可以指示编译器,编译器会将接续符删除,跟在接续符后面的字符自动接续到前一行。在接续单词时,接续符后面不能有空格,接续符的下一行之前也不能有空格。通常接续符使用在定义宏代码块时使用。

2、单引号和双引号

C语言中单引号用来表示字符字面量,双引号用来表示字符串字面量

‘a’表示字符字面量,占一个字节大小,’a’+1表示’b’,”a”表示字符串字面量,”a”+1表示指针运算,结果为指向”a”字符串的结束符’\0’。

char c = “hello”;

字符串字面量“hello”的地址赋值给字符变量c,由于地址占用四个字节空间,赋值给字符类型后会发生类型截断。

七、++、--、三目运算符

1、++、--

++、--参与混合运算的结果是不确定的。

2、三目运算符

三目运算符返回变量的值,而不是变量本身。根据隐式类型转换规则确定返回值类型。

int a = 1;

int b = 2;

int c = 0;

c = a < b ? a : b;

(a < b ? a : b) = 3;//error

*(a < b ? &a : &b) = 3;//ok

八、宏

1、宏定义

#define由预处理器处理,直接进行文本替换,不会进行语法检查,#define定义的宏可以出现在程序的任意位置

#define定义的宏常量本质为字面量

宏由预处理器处理,编译器不知道宏的存在。

宏表达式没有任何的调用开销,不能出现递归定义

编译器内置的宏:

__FILE__:被编译的文件名

__LINE__:当前行号

__DATE__:编译时的日期

__TIME__:编译时的时间

__STDC__:编译器是否遵循标准C规范

#defineLOG(s) printf("[%s] File:%s, Line:%d %s \n", __DATE__, __FILE__, __LINE__, s)

2、条件编译

条件编译是预编译指示命令,用于控制是否编译某段代码。预编译器根据条件编译指令有选择的删除代码,编译器不知道代码分支的存在。

条件编译可以解决头文件重复包含的编译错误

#ifndef _FILE_H_

#define _FILE_H_

//source code

#endif

条件编译通过不同的条件编译不同的代码,生成不同条件的目标,实际工程中可以使用条件编译将同一份工程代码生成不同的产品线或是区分产品的调试和发布版。

3、#error

#error用于生成一个编译错误消息

语法:#error message

message不需要双引号

#ifndef __cplusplus

#error This file should be processed with C++ compiler.

#endif

编译过程中产生错误信息意味着编译将终止,无法生成最终的可执行程序

4、#line

#line用于强制指定新的行号和编译文件名,并对源程序的代码进行重新编号

#line number filename

#line本质上是对__LINE__和__FILE__宏的重定义

5、#progma

#progma用于指示编译器完成某些特定的动作,所定义的很多关键字和编译器有关,在不同编译器间是不可以移植的。预处理器将忽略不认识的#progma指令,不同的编译器可能会对#progma指令的解释不同。

#progma message

编译时输出消息到编译器输窗口,用于提示信息

#if defined(ANDROID20)

#pragma message("Compile Android SDK 2.0...")

#define VERSION "Android 2.0"

#elif defined(ANDROID23)

#pragma message("Compile Android SDK 2.3...")

#define VERSION "Android 2.3"

#elif defined(ANDROID40)

#pragma message("Compile Android SDK 4.0...")

#define VERSION "Android 4.0"

#else

#error Compile Version is not provided!

#endif

与#error不同,#progma massage仅代表一条编译消息,不代表编译出错

#progma once

#progma once用于保证头文件只被编译一次,和编译器相关,编译器不一定支持。

工程代码使用如下:

#ifndef_FILE_H_

#define _FILE_H_

#progma once

#endif

#progma pack

#progma pack用于指定内存对齐方式,一般成对使用

#progma pack(n)

//source code

#progma pack()

6、#运算符

#运算符用于在预处理期将宏参数转换为字符串,只能在宏定义中有效

#define STRING(x) #x

printf("%s\n", STRING(Hello world!));

#define CALL(f, p) (printf("Call function %s\n", #f), f(p))

7、##运算符

##运算符用于在预处理期粘连两个标识符,只在宏定义中有效

#define NAME(n) name##n

int main()

{

int NAME(1);

int NAME(2);

NAME(1) = 1;

NAME(2) = 2;

printf("%d\n", NAME(1));

printf("%d\n", NAME(2));

return 0;

}

时间: 2024-12-10 10:30:20

嵌入式 Linux C语言——C语言基础的相关文章

【分享】4412开发板-嵌入式Linux开发需要掌握的基础知识和技能

本文转自迅为电子论坛:http://www.topeetboard.com 1.Linux 基础 安装Linux操作系统 Linux文件系统 Linux常用命令 Linux启动过程详解 熟悉Linux服务能够独立安装Linux操作系统 能够熟练使用Linux系统的基本命令 认识Linux系统的常用服务安装Linux操作系统 Linux基本命令实践 设置Linux环境变量 定制Linux的服务 Shell 编程基础使用vi编辑文件 使用Emacs编辑文件 使用其他编辑器 2.Shell 编程基础

【嵌入式Linux+ARM】硬件相关基础知识(门电路_UART_I2C_SPI)

比较基本的一些知识,就算再怎么水,一些基本的电路常识还是需要懂的,需要自己慢慢的积累,实际工作中需要好好积累硬件相关的知识: 正文开始: 1.门电路 与门.或门.非门的几个标志需要记住,特别是后面两个不要混淆,自己画了一遍: 实际电路中可以组合使用:如与非门.或非门等复杂电路. 这些是比较简单的,做个简单的复习: 与门:1 1-->1      1 0-->0      0 1-->0     0 0-->0 或门:1 1-->1      1 0-->1      0

嵌入式linux C++语言(二)——C++对C语言基础语法的扩展

嵌入式linux C++语言(二)--C++对C语言基础语法的扩展 C++是基于C语言扩展发展而来的面向对象的程序设计语言,本文将主要讨论C++语言基于C语言扩展的方面. 一.类型增强 1.类型检查更严格 在C语言中: const int a = 100; int *p = &a; 在C++语言中: const int a = 100;//必须在定义的时候初始化 const int *p = &a; 在C++语言中不能隐式转换数据类型. error: invalid conversion

嵌入式linux C++语言(一)——C++简介

嵌入式linux C++语言(一)--C++简介 一.C++简介 C语言作是结构化和模块化的语言,适合处理较小规模的程序.对于复杂的问题,规模较大的程序,需要高度的抽象和建模时,C语言并不合适.为了解决软件危机, 20世纪80年代, 计算机界提出了OOP(object oriented programming)思想,支持面向对象的程序设计语言应运而生.Smalltalk 就是当时问世的一种面向对象的语言.在实践工作中,由于C语言的广泛使用,在C语言的基础上根据面向对象的思想发展了C语言,形成了C

嵌入式linux C++语言(六)——运算符重载

嵌入式linux C++语言(六)--运算符重载 运算符重载的本质是函数重载. 一.重载基础 1.运算符重载的语法 返值类型 operator 运算符名称(形参表列){    重载实体;} 2.友元重载 可以将运算符重载函数声明位友元函数 #include <iostream> using namespace std; class Complex { public:     Complex(float x=0, float y=0)         :_x(x),_y(y){}     voi

嵌入式linux C语言(一)——位运算的使用

嵌入式linux C语言(一)--位运算的使用 ARM是内存与IO统一编址,SoC中有很多控制寄存器,通过对这些寄存器进行位运算对这些控制寄存器进行设置,进而控制外设功能.在修改寄存器某些位的过程中不能修改其他的位. 一.位运算基础 C语言基本的位操作符有与.或.异或.取反.左移.右移六种位运算符.如下表所示: 符号 描述 运算规则 & 与 两个位都为1时,结果才为1 | 或 两个位都为0时,结果才为0 ^ 异或 两个位相同为0,相异为1 ~ 取反 0变1,1变0 << 左移 各二进位

嵌入式Linux C语言(四)——指针与数组

嵌入式Linux C语言(四)--指针与数组 数组是C语言内建的数据结构,彻底理解数组及其用法是开发高效应用程序的基础.数组和指针紧密关联,但又不是完全可以互换. 一.数组简介 数组是能用索引访问的同种类型元素的连续集合.数组的元素在内存中是相邻的,中间不存在空隙,数组的元素是相同类型的. 1.数组的解读 数组的定义:int a[10] = {0,1,2,3,4,5}; a[0]:数组的第一个元素,首元素(做左值时表示第0个元素的内存空间) &a:数组的首地址,是常量,不能做左值,类型等同int

嵌入式Linux C语言(二)——指针

嵌入式Linux C语言(二)--指针 指针是C语言中广泛使用的一种数据类型,是C语言的灵魂.指针提供了动态操控内存的机制,强化了对数据结构的支持,而且实现了访问硬件的功能.学习指针是学习C语言中最重要的一环,能否正确理解和使用指针是我们是否掌握C语言的一个标志. 一.指针的概念 在计算机中,所有的数据都是存放在内存中的,一般把内存中的一个字节称为一个内存单元,不同的数据类型所占用的内存单元数不一样,如int占用4个字 节,char占用1个字节.为了正确地访问内存单元,必须为每个内存单元编上号.

嵌入式linux C++语言(四)——类与对象

嵌入式linux C++语言(四)--类与对象 类的设计和使用如下: #include <iostream>#include <stdlib.h>#include <stdio.h>#include <string.h>using namespace std;class Stack{public:    Stack(int size=1024);    ~Stack();    void init();    bool isEmpty();    bool