No.1 C语言开发基础知识
gcc编译器:
gcc(GNU Compiler Collection)是GNU推出的多平台编译器,可将C、C++源程序编译连接成可执行文件,支持以下后缀:
- l.c c语言源代码
- l.h 程序所包含的头文件
- l.i 已经预处理过的C源代码文件
- l.s 汇编语言源代码文件
- l.o 编译后的目标文件
实例解析:
gcc –o hello hello.c
-o 输出可执行文件
-c 只要求编译器输出目标代码(.o文件),而不必输出可执行文件
-g 用于调试
./hello 查看输出结果
gcc做为一个交叉编译器,如何生成ARM平台的执行程序?
经过gcc编译器最后生成的一大堆机器码只有Linux操作系统认识,但是我们做ARM裸机实验时Soc上是没有linux操作系统的,所以这时候ARM裸机只认识经过ARM指令集生成的机器码。这时候我们要想让可执行文件在ARM裸机上运行就需要使用arm-linux-gcc (交叉编译工具)编译生成的可执行文件。字面理解arm-linu-gcc的意思也就是编写环境是使用Linux主机编写的,然后编译后要在ARM上运行的可执行文件。
附上转载原文出处链接。原文链接:https://blog.youkuaiyun.com/yz_cfm/article/details/76998958
数据的表示 :
数值数据的表示(包括二进制、八进制、十进制、十六进制)
基数与各数位的权:
基数是指该进位制中允许选用的基本数码的个数。如十进制数,基数为10, 可选用0、1、2……9共10个不同数码中的任何一个。而位权的大小是以基数为底,数字所在位置的序号为指数的整数次幂。
基数为2的进位制叫二进制
二进制只有0、1两种数码,计数逢2进位
基数为16的进位制叫十六进制
十六进制有0、1、2…9、a、b、c、d、e、f 共16种数码,计数逢16进位
非数值数据的表示
非数值数据包括文字、符号、图像、语言和逻辑信息等,也都是以0、1形式存在。
字符数据在机器内也被变换成二进制编码的形式。国际上普遍采用的一种编码是美国国家信息交换标准代码,简称为ASCII码。
char unsigned char 1字节 00000000~11111111 0~255
No.2 数据类型
基本数据类型
逻辑类型。只有两个量true和false,表示逻辑真值和逻辑假值。
整数类型。包括char, short, int和long。
浮点类型。包括float和double。
void类型。主要用于说明不返回值的函数或指向任一类型的指针等。
bool类型
代码示例:
#include <stdio.h>
#include <stdbool.h>
int main(int argc, char *argv[])
{
_Bool a;
// bool a;
a = true;
a = false;
a = -1;
if (a) {
printf("true %d\n",a);
}
else {
printf("false %d\n",a);
}
return 0;
}
char类型
代码示例:
#include <stdio.h>
#include <stdbool.h>
int main(int argc, char *argv[])
{
unsigned char ch;
ch = -1;
printf("ch=%d %c\n", ch, ch);
return 0;
}
整型
代码示例:
#include <stdio.h>
#include <limits.h>
int main(int argc, char *argv[])
{
printf("%d %d %d %d\n", sizeof(_Bool), sizeof(char), sizeof(short), sizeof(int));;
printf("char:%d-%d\n", SCHAR_MIN, SCHAR_MAX);
printf("shor:%d-%d\n", SHRT_MIN, SHRT_MAX);
printf("int:%d-%d\n", INT_MIN, INT_MAX);
return 0;
}
浮点型
代码示例:
#include <stdio.h>
int main(int argc, char *argv[])
{
float a = 3.14;
double b = 4.5;
printf("%f %lf\n", a, b);
return 0;
}
强制进行数据类型转换
强制数据类型的转换是指采用某种方式将某种数据类型强制转换成指定的数据类型。包括显式的数据类型转换,和隐式的数据类型转换。
强制类型转换符后面的表达式如存在复杂运算,就一定要用小括号括起来。
强制类型转换符是一种不安全的转换,一般都是将高级类型转换成低级类型,要丢失数据的精度。
强制类型转换并不改变表达式中变量的数据类型和其值。
代码示例:
#include <stdio.h>
#include <limits.h>
int main(int argc, char *argv[])
{
int a = 2, b = 3;
float c = 3.14;
int sum;
sum = (int)(a + b + c);
printf("%d\n", sum);
return 0;
}
No.3 常量及变量
整型常量:
常量是指在程序运行期间其数值不发生变化的数据。整型常量通常简称为整数。整数可以是十进制数、八进制数和十六进制数。例如,十进制的数值3356可以有下列二种不同的表示形式:八进制数 06434 十六进制数 0xd1c。
浮点常量:
浮点常量又称为实数,一般含有小数部分。在C语言中,实数只有十进制的实数,分为单精度和双精度。实数有两种表示方法, 即一般形式和指数形式。一般实数:3.5,-12.5.指数形式:指数形式的实数一般是由尾数部分、字母e或E和指数部分组成。 当一个实数的符号为正号时,可以省略不写,其表示的一般形式如下:1.176e+10 表示 1.176×1010 -3.5789e-8 表示 -3.5789×10-8 这通常表示特别大或特别小的数。
字符常量:
字符常量是指一个单一字符, 其表示形式是由两个单引号包括的一个字符。例如A’, ‘a’。字符常量具有数值。字符常量的值就是该字符的ASCII码值。可以把字符常量看做一个字节的正整数。
字符串常量:
所谓字符串常量是指用双引号括起来的一串字符来表示的数据。(字符串以\0结尾) 例如: “9” == ‘9’,’\0’ 。
标识常量:
所谓标识常量是指用标识符代替常量使用的一种常量, 其名称通常是一个标识符。标识常量也叫符号常量,一般用大写英文字母的标识符。在使用之前必须预先定义。(注意这里仅仅是进行了替换,但是不会提前对预定义的常量进行运算)
编程实例:
一个水分子的质量约为3.0*10-23g,1夸脱水大约有950g,编写一个程序,要求输入水的夸脱数,然后显示这么多水中包含多少水分子。
#include <stdio.h>
#define W 3.0e-23
#define C 950
int main(int argc, char *argv[])
{
float n;
printf("input n:");
scanf("%f", &n);
n = n * C / W;
printf("%f %e\n", n, n);
return 0;
}
常量思考问题:
1. 用预处理指令 #define 声明一个常数,用以表明一年中有多少秒(忽略闰年问题)
#define SECONDS 60
#define MINUTES 60
#define HOURS 24
#define DAYS 365
#define SECONDS_PER_YEAR DAYS*HOURS*MINUTES*SECONDS //31536000
2. 以下程序的输出结果是( )
#define M(x,y,z) x*y+z
void main(void){
int a=1, b=2, c=3;
printf("%d\n", M(a+b,b+c,c+a));}
A.19 B.17 C.15 D.12
#define M(x,y,z) x*y+z
void main(void){
int a=1, b=2, c=3;
printf("%d\n", M(a+b,b+c,c+a));
}
//M(a+b,b+c,c+a)) == a + b * b + c + c + a == 1+2*2+3+3+1 == 12
储存变量:
变量在程序中用变量名表示。变量名由用户根据其用途任意命名。变量名由字母、数字、下划线组成,不能以数字开头,不能和C的关键字重名。 在程序运行时,变量占据存储空间的大小由其数据类型决定。变量在内存空间中的首地址,称为变量的地址。
变量说明的一般形式是:<存储类型> <数据类型 > <变量名> ; <存储类型>是关键词auto、register、static和extern;<数据类型>可以是基本数据类型,也可以是自定义的数据类型。
AUTO存储类型:
auto说明的变量只能在某个程序范围内使用,通常在函数体内或函数中的复合语句里(默认是随机值)。在函数体的某程序段内说明auto存储类型的变量时可以省略关键字auto。
#include <stdio.h>
int main(int argc, char *argv[])
{
if (1) {
int a;
printf("a=%d\n", a); //输出的值为随机值,证明auto变量不初始化的值是未知的
}
// printf("out if:a=%d\n", a);
return 0;
}
REGISTER存储类型:
register称为寄存器型,register变量是想将变量放入CPU的寄存器中,这样可以加快程序的运行速度。register变量必须是能被CPU所接受的类型。这通常意味着register变量必须是一个单个的值,并且长度应该小于或者等于整型的长度。不能用“&”取地址符号来获取register变量的地址。
#include <stdio.h>
int main(int argc, char *argv[])
{
register int a = 10;
//printf("out if:a=%d %p\n", a, &a);报错,显示没有地址,证明寄存器变量无法取地址
return 0;
}
EXTERN存储类型:
当变量在一个文件中的函数体外说明,所有其他文件中的函数或程序段都可引用这个变量。extern称为外部参照引用型,使用extern说明的变量是想引用在其它文件中函数体外部说明的变量。static修饰的变量,其它文件无法使用。
//int global_a = 100;
static int global_a = 100;
//验证static变量声明的变量无法被联合编译并且调用
#include <stdio.h>
extern int global_a;//extern变量,联合编译另一个文件的变量值,并且调用
int main(int argc, char *argv[])
{
printf("global_a=%d\n", global_a);
return 0;
}
STATIC存储类型:
static变量称为静态存储类型的变量,既可以在函数体内,也可在函数体外说明。(默认是0)局部变量使用static修饰,有以下特点:在内存中以固定地址存放的,而不是以堆栈方式存放。只要程序没结束,就不会随着说明它的程序段的结束而消失,它下次再调用该函数,该存储类型的变量不再重新说明,而且还保留上次调用存入的数值。
#include <stdio.h>
int main(int argc, char *argv[])
{
int i = 1;
while (i < 5) {
// int a;
static int a;
a++;
printf("a=%d\n", a);//和auto变量进行对比,验证了static变量在声明时默认值为0
i++;
}
return 0;
}
问题:修饰局部变量时,auto和static和register有什么区别?
- auto关键字是默认的存储类别说明符,用于定义自动变量。
- static关键字用于定义静态变量,具有静态存储期。
- register关键字用于提示编译器将变量存储在寄存器中,但实际是否存储在寄存器中取决于编译器的优化策略。
变量思考问题:
1. 简述使用static关键字修饰一个局部变量时与不使用该关键字声明一个局部变量有什么异同。
当使用static关键字修饰一个局部变量时,只要程序还没结束,那么该变量就不会随着说明它的程序段的结束而消失,它下次再调用该函数,该变量不再重新说明,而且还保留上次调用存入的数值。我的理解为该关键字会声明一个静态变量,程序不结束,该变量的数值为上次调用存入的数值。同时如果不给其赋值,则默认为赋值0。
2. 使用extern关键字声明的变量代表什么?
当使用extern关键字在一个文件中的函数体外说明一个变量,那么,所有其他文件中的函数或程序段都可引用这个变量。当使用其他文件引用该变量时,需要同时编译两个文件。(和static的区别,static修饰的全局变量,其它文件无法使用)
No.4 运算符
算术运算符:
#include <stdio.h>
int main(int argc, char *argv[])
{
float a = 15, b = 8, c;
c = a + b;
printf("c=%f\n", c);
c = a * b;
printf("c=%f\n", c);
c = a / b;
printf("c=%f\n", c);
//c = a % b;
//printf("c=%f\n", c); 两个float无法取余
return 0;
}
int main(int argc, char *argv[])
{
int x = 15, y = 5, z;
#if 0
y = ++x;
printf("x=%d y=%d\n", x, y);
y = x++;
printf("x=%d y=%d\n", x, y);
#endif
z = ++x + y++;
printf("x=%d y=%d z=%d\n", x, y, z);
return 0;
}
关系运算符:
逻辑运算符:
#include <stdio.h>
int main(int argc, char *argv[])
{
int x = 5, y = 4, z;
if (x != 5 || y++) { // true z = x + y = 5 + 5 = 10
//if (x != 6 && y++) {
z = x + y;
printf("true:x=%d, y=%d,z=%d\n", x, y, z);
}
else {
z = x - y;
printf("false:x=%d, y=%d,z=%d\n", x, y, z);
}
return 0;
}
位运算符:
逻辑取反:
逻辑或:
逻辑与:
异或运算:
移位运算:
复合运算符:
三目运算符:
#include <stdio.h>
int main(int argc, char *argv[])
{
int x = 70, y = 10;
//y = x++ > 70 ? x + y : 5; // x = 71 ,5
y = ++x > 70 ? x + y : 5; // x =71, x + y = 81
printf("x=%d y = %d\n", x, y);
return 0;
}
特殊运算符:
逗号运算符以及sizeof运算符
#include <stdio.h>
int main(int argc, char *argv[])
{
int a = 5;
long b = 3;
long long c = 9;
printf("%d %d %d\n", sizeof(a), sizeof(b), sizeof(c));
printf("%d %d %d\n", sizeof(int), sizeof(long), sizeof(long long));
return 0;
}
逗号运算符:
#include <stdio.h>
int main(int argc, char *argv[])
{
int x = 1, y = 0, z = 0, a;
a = (x += y == z, y = x + 2, z = x + y + x > 0);
printf("x=%d y = %d z=%d a=%d\n", x, y, z, a);
//逗号运算符,从左至右运行,将最后一个运算式的值赋给变量
return 0;
}
运算符优先级:
注意:逗号运算符的优先级是最低的,而且单个等号=的优先级是低于双等号==
#include <stdio.h>
int main(int argc, char *argv[])
{
int x = 1, y = 0, z = 0;
// if (x++ && y++ || ++z)
if (!(x+1)>0 && y++ || ++z)
printf("true:%d %d %d\n", x, y, z);
else
printf("false:%d %d %d\n", x, y, z);
return 0;
}
思考问题:
如何把一个无符号数的某位快速置1?
如果想将某个无符号的数的某一位至1的话,那么只需要将这个数和将1左移这个位数减一位的值进行或运算即可,那么假设这个数为X,需要将X的Y位至1,则有X = X | (1 << (Y - 1))
如何把一个无符号数的某位快速清0?
如果将某一位进行至0,则有X = X & (~(1 << (Y - 1))),如果想要将一个10进制的数进行改为16进制,X/16的操作可以理解为这个数右移4位,(一个数左移一位等于乘2,右移等于除2),X%16可以理解位这个数和0xf进行&运行就好。
实践问题:
1. 浮点数可以参与除法运算么?那么取余运算呢?
2. 在位运算中,某操作数右移3位,其结果相当于?
A.操作数除以6 B.操作数乘以6 C.操作数除以8 D.操作数乘以8
1.浮点数可以参与除法运算,但是取余运算只能用于整数之间的运算,不能用于浮点数。
2.C
有以下程序 unsigned int a = 0x20; a = a >> 2; a = ~a; 执行完该程序后变量a的值是多少?
0xfffffff7