#include<stdio.h>
int main(){
#ifdef ABC
printf("----------------------%s---------------\n",__FILE__);
#endif
printf("hello,world!\n");
return 0;
}
/*
编译:
gcc -DABC -o 001 001.c
运行输出:
----------------------001.c---------------
hello,world!
*/
#include<stdio.h>
#define Day(x) #x //相当于把x的两边加上双引号
#define HDay(x) variety_name##x //相当于对变量名进行控制
int main(){
printf(Day(abc\n));
int variety_name1 = 10;
int variety_name2 = 20;
printf("%d\n",HDay(1));
printf("%d\n",HDay(2));
return 0;
}
/*
输出:
abc
10
20
*/
/*
sizeof是编译器提供给我们的一个关键字,在什么环境下(包括裸板)都可以使用
所以sizeof并不是一个函数
*/
#include<stdio.h>
int main(){
int a = 3;
printf("%d\n",sizeof a);
printf("%d\n",010);//以0开始的数字被编译器当做8
return 0;
}
C操作对象:资源/内存(内存类型的资源,LCD缓存、LED灯)
C语言如何描述这些属性那?
资源属性【大小】
限制内存(土地)的大小,关键字
int a;
sizeof(a)占用多大的内存?数据类型的大小涉及到int,char时,不是一个定义,
这时一个和编译器有关的答案,数据类型的大小一般由默认值指定。
硬件芯片操作的最小单位:
bit 1 0
软件操作的最小单位:8bit == 1B
4M 4Mbit Kbit/s KB/s
char a; bit a
应用场景:
硬件处理的最小单位
char buf[xx]; int buf[x];
ASCII表 8bit
溢出问题:
1 = 2
2 = 4
3 = 8
4 = 16
...
8bit == 256
char a = 300;这是错误的
=============================
编译器最优的处理大小:
系统一个周期所能接受的最大处理单位,int
32bit 4B int
16bit 2B int
int a; char a;
==========================
类型常量
char a = 300, 300l 300L(用来描述一个int型)
2B 65535
int a = 65535;
进制表示:
10
十进制 八进制 十六进制 二进制
3bit 8进制
111 0x7
1000 0x8 int a = 010 //8
12 001 010
4bit 16进制 int a = 0x10 //16
long 和 short是补齐char和int的短板
long是扩展的,long,long long
===========================
无符号: 数据
有符号: 数字
内存空间的最高字节 是符号位 还是数据
unsigned int a;
char a;
>>
char a = -1; 0xff
a >> 1,有符号数右移会进行符号位的扩展
unsigned char b = -1;
b>>1,无符号数据的右移会导致在最高位使用0进行填充
*/
#include<stdio.h>
//#define MOD 0
//#define TUE 1
//#define WED 2
//使用枚举的方式和使用上面的使用define定义的方式效果是相同的
//enum abc{MOD,TUE,WED};//这种方式按照0 1 2 的方式赋值
enum abc{MOD=100,TUE,WED};//这种方式按照100 101 102 的方式赋值
//enum {MOD=100,TUE,WED};//也可以去掉前面的变量名abc
int main(){
printf("the %d\n",MOD);//这条语句的输出结果是:the 0
printf("the %d\n",WED);//这条语句的输出结果是:the 0
enum abc a1;
printf("the sizeof of a1 is %lu\n",sizeof a1);//输出的结果是:the a1 is 4
enum abc a2 = WED;
printf("the sizeof of a2 is %lu,%d\n",sizeof a2,a2);//输出的结果是:the sizeof of a2 is 4,102
enum abc a3 = 800;
printf("the sizeof of a3 is %lu,%d\n",sizeof a3,a3);//输出的结果是:the sizeof of a3 is 4,102
对于gcc编译器来说,不管有多少个,enum是一个语义的范畴,
可以把enum当做是任何人之间交流的符号,相当于使用一个组合来描述一个人。
比如驱动变成,芯片a支持3种功能,芯片b支持4种功能。如传的1表示功能一,
传的2表示功能二。所以可以把跟芯片有关的功能可以起个名字,另外一个芯片再
定义一个共用体。
应用举例:可以看出,使用数字来表示功能,让资源和程序员之间更好地交流
enum c509cmd{TotalReset = 0<<11,SelectWindow = 1<<11,StartCoax = 2<<11};
*/
}
#include<stdio.h>
typedef int time_t;
int main(){
//len_t a = 170;
//time_typedef t = 3600;
//当我们看到 xxx_t时,第一反应是这个是使用typedef定义的变量,这在linux高级编程中会有体现,这个是不成文的规定
time_t time1 = 360;
printf("%d秒\n",time1);//输出:360秒
return 0;
}
#include<stdio.h>
int main(){
对内存资源存放位置的限制
类型修饰符:auto,register,static,const,extern,volatile
如:auto char a;
区域如果在{}内部,就认为是栈空间
auto int a;
register int a;//限制变量定义在寄存器上的修饰符
*/
return 0;
}
char *p
//const char和char const修饰的是指针指向的内容。而指针仍然具有指向不同的内存单元的能力。
const char *p//字符串。这两种用法相同,这种推荐。指针p指向的内容是只读的,尽管在黑客眼里并不完全是这样。
char const *p;
//下面表示指针只具有单指向。一般是硬件资源的定义。如显卡的缓存的地址是固定的
char * const p;//这两种用法相同,这种推荐,也就是说,指向的地址不变,地址内的内容可变
char *p const;
const char * const p;//ROM,地址和地址指向的内容都不可变
*/
#include<stdio.h>
int main(){
char *p1 = "hello world!\n";//双引号是常量,改变时会发生段错误。双引号相当于const char
//*p1 = 'a';//这句会发生段错误
char buf[] = {"hello world!\n"};
char *p2 = buf;
*p2 = 'a';
printf("%s\n",buf);
return 0;
}
#include<stdio.h>
switch(day){//这种方式比较浪费时间并且比较麻烦
case 1:
fun1();
break;
case 2:
func2();
break;
}
下面的方式是改进:
int (*p[7])(int,int);//函数名是地址这个思想很重要,知道了地址就可以进行调用
p[0] = fun1;//这个叫做注册
p[1] = fun2;
p[day](10,20);//这个叫做回调
*/
int main(){
int (*myshow)(const char *,...);
printf("the printf is %p\n",printf);
myshow = printf;//看到printf,要首先想到这是一个地址
//myshow = (int (*)(const char *,...))0x12345657;//如果这个printf换成一个十六进制的地址,那么需要通过强制类型转换转换为合适的类型,由此可见,函数名就是一个地址。
myshow("==========================\n");
return 0;
}
#include<stdio.h>
struct abc{
char a;
short e;
int b;
};
struct my{
char a;
int e;
short b;
};
int main()
{
struct abc buf;
struct my myy;
printf("%d,%d\n",sizeof(buf),sizeof(myy));
return 0;
}
volatile: 防止优化指向内存地址
volatile char *p;//对指针指向的内容进行修饰
什么类型,变量的名称是谁
char *name_t; name_t是一个指针,指向一个char类型的内存
typedef char *name_t; name_t是一个指针类型的名称,指向了一个char类型的内存
int *p = xxx;[0x12]
p+1 是[0x12 + 1 * (sizeof(*p))],也就是说,p+1指向下一个单位
指针的加法运算,实际上加的是一个单位
p++ p-- 更新地址。原来的值可能就没有了
地址内容的标签访问方式,取出标签里面内容的值。[]方括号的使用。变量名[n],n是ID或者标签。非线性访问,对多个单位跨越访问。
强制类型转换,建议大量使用
int a = 0x12345678;
int b = 0x99999999;
int *p1 = &b;
char *p2 = (char *)&b;//这种方式是以char的方式读取内存
*/
static的用法
应用场景:
1)函数内部的变量
2) 函数外部的变量
3)函数的修饰符
int fun() ===> static int fun()
extern 外部声明
const 代表常量的定义,const是C语言的软肋,希望是常量,const int a = 100
但是,只读的变量,但是,还是可以通过某些方法改变这些值。只不过不能通过在编译器中进行显式的修改。
volatile 告知编译器编译方法的关键字,不优化编译
修饰变量值的更改不仅仅通过软件,也可以通过其他技术(硬件,外部的用户)
int a = 100;//如果是接收按键的输入
while(a == 100 );
mylcd()
------------------
[a]: a的地址
f1: LDR R0,[a]
f2: CMP R0,100
f3: JMPeq f1 -----> 优化之后:JMPEQ f2
f4: mylcd()
上面4行代码的哪一行是影响执行效率的,答案是f1,和内存地址打交道。f1有么有必要存在?
不优化:
优化:
运算符
*,不同的运算符的场景是不同的。
CPU
int a = b * 10;//有些cpu没有实现乘法的硬件,因此要利用软件的模拟方法来实现乘法
int a = b + 10;
乘除法有时候能不用先不用
% 用于求模
*/
#include<stdio.h>
int fun(){
static int a = 12;
return 1;
}
int main(){
static int a = 10;
printf("hello,world\n");
return 0;
}
char *p
//const char和char const修饰的是指针指向的内容。而指针仍然具有指向不同的内存单元的能力。
const char *p//字符串。这两种用法相同,这种推荐。指针p指向的内容是只读的,尽管在黑客眼里并不完全是这样。
char const *p;
//下面表示指针只具有单指向。一般是硬件资源的定义。如显卡的缓存的地址是固定的
char * const p;//这两种用法相同,这种推荐,也就是说,指向的地址不变,地址内的内容可变
char *p const;
const char * const p;//ROM,地址和地址指向的内容都不可变
*/
#include<stdio.h>
int main(){
char *p1 = "hello world!\n";//双引号是常量,改变时会发生段错误。双引号相当于const char
//*p1 = 'a';//这句会发生段错误
char buf[] = {"hello world!\n"};
char *p2 = buf;
*p2 = 'a';
printf("%s\n",buf);
return 0;
}
/*
实参 传递给 形参
传递的形式:
拷贝(包括值传递和地址传递也是)
逐一拷贝的概念
*/
#include<stdio.h>
void myswap(void){//传递的参数为空
}
void myswap2(int buf){//传递的参数不为空
printf("buf is %x\n",buf);//以十六进制的形式输出
}
void myswap3(char buf){
printf("the buf is %x\n",buf);
}
int main(){
int a = 20;
myswap();
myswap2(0x123);// buf = 0x123
myswap3(0x1234);// buf = 0x1234,但是建议实参和形参之间的内存大小的一致性
char *p = "hello world\n";//p中存放的是地址
printf("the p is %x,sizeof(p)=%d\n",p,sizeof(p));
myswap2(p);
return 0;
}
/*
运行结果:
buf is 123
the buf is 34
the p is 8846a79e,sizeof(p)=8
buf is 8846a79e
*/
/*
值传递
地址传递
*/
#include<stdio.h>
void swap(int a,int b){//值传递
int c;
c = a;
a = b;
b = c;
}
void swap_2(int *a,int *b){//地址传递,指针是标识变量的唯一...
//上层调用者让下层子函数修改自己空间值的方式
int c;
c = *a;
*a = *b;//使用指针来改变内存单元中的内容
*b = c;
}
int main(){
int a = 20;
int b = 30;
int c;
printf("the a is %d, the b is %d\n",a,b);
// c = a;
// a = b;
// b = c;
swap(a,b);//传递进去的是值
printf("after swap,the a is %d, the b is %d\n",a,b);
//下面是地址传递
swap_2(&a,&b);//传递进去的是指针(地址)
printf("after swap_2,the a is %d, the b is %d\n",a,b);
return 0;
}
连续空间的传递
传感器采集的数据,交给专门的处理函数来处理
处理完成之后告诉应用程序
1.数组
数组名 --- 标签
实参:
int abc[10];
fun(abc);//实参也是地址
形参:
void fun(int *p);//那么形参也就是指针
void fun(int p[10]);//这种是给人看的,实际上p还是和上一行一样,是地址
2.结构体
结构体变量,建议使用指针
struct abc{int a;int b;int c;};
struct abc buf;
实参:
fun(buf);
fun(&buf);//推荐
形参:
void fun(struct abc a1) //这种方式可能存在多分拷贝,浪费内存
void fun(struct abc *a2) //推荐,节约内存
构成连续空间的两个主要因素,虽然语法不太一样。
需要考虑的问题:连续空间的可写性
void fun(char *p);//这种形式定义,p指向的内存很大可能可变
void fun(const char *p);//这种形式定义:p指向的内存绝对不可变
const char *p;//只读空间,为了空间看看。这种绝对变不了
以后看到:char *p;//则很大可能性该空间可以修改
strcpy(char *dest,const char *src);
strncpy(char *dest,const char *src,int n);
*/
#include<stdio.h>
void fun(char *c){
while(*c){
printf("%c",*c);
c++;
}
printf("\n");
}
void fun_2(char *c){
*c = 'i';
}
int main(){
fun("hello,world");//双引号,常量区,修改则出现段错误。实际上,传递进去的实参是首地址
char buf[] = "hello,world\n";
fun_2(buf);
printf("%s",buf);
return 0;
}
/*
输出:
hello,world
iello,world
*/
1、修改 int *,char *,...
2、空间传递
2.1 子函数查看空间里的情况 const
2.2 子函数反向修改上层空间里的内存(这次将这个)
空间的分类:字符空间和非字符空间
p[1000],p[100],p[10000],可以无穷访问?
字符空间:结束标志不同
空间:空间首地址、结束标志
结束标志:内存里存放了0x00(1B),字符空间。非字符空间中0x00不能作为结束标志
----->
void fun(char *p){
int i = 0;
p[i] i++;//从头找到尾,跟循环有关系,找到结束标记,while循环
while(p[i] != 0){
p[i]操作 p[i] = x; a = p[i] + -
i++;
}
}
strcpy(char *dest,const char *src)
"" --> 初始化const char *
char buf[10] ---> 初始化char *
非字符空间
void fun(unsigned char *p)
{
p[100] = xx
p[1000] = yy;//非字符空间,我们最关心的还是结束标记
即:数量,一般都是以字节为单位
}
void fun(unsigned char *p,int len)//一般这样写
{
int i;
for(i = 0;i < len;i++){
p[i] = ; a = p[i] //++++++ -----
}
}
使用void *,形参化的类型。void *,非字符空间或者数据空间的标识
void还得跟一个大小,没有结束标记
memcpy(void *dest,const void *src,size_t n)
int main(){
struct sensor_data buf;
int buf1[100];
fun(&buf,sizeof(buf)*1)
}
ssize_t recv(int sockdd,void *buf,size_t len,int flags)
//遇到void *,尽量开unsigned char。char buf[100],这里应当定义为unsigned char buf[100]
recv(buf); 00 00 hello
printf("%s",buf);//且不要使用%s
------------------------------------------------------------
int fun1(void) int a = 0; a = fun1();//这样可以修改a
void fun2(int *p) int a = 0; fun2(&a);//这样可以修改a
int fun(int *) //承上启下改两个
int *fun1(void);//改写成
int main()
{
int *p;
p = fun1();//可以认为p被改变了
}
void fun2(int *p)
int main()
{
int *p;
//fun2(p);//p是值传递,不能改变p,尽管p指向的内容不敢保证不变
fun2(p);
}
void fun3(int **p)//引申
int main()
{
int *p;
fun3(&p);//传入的是p的地址,p本身是一个指针,这样p的内容就能够发生改变了
}
----------------------------------------------------------
指针作为空间返回的唯一数据类型
int *fun();
作为函数的设计者,必须保证函数返回的地址所指向的空间是合法的【不是局部变量】
使用者:
int *fun();
int *p = fun();
int p[100];
---------------------------------------
char *fun(void)
{
//static char buf[] = "hello world";
char *s = (char *)malloc(100)
strcpy(s,"hello world");
return s;
}
int main(){
char *p;
p = fun();
printf("the p is %s\n",p);
free(p);
return 0;
}
*/
#include<stdio.h>
char *fun(void){
char buf[] = "hello world";
//return "hello world";
return buf;//buf占用的是fun的栈空间,fun函数执行结束是,也就销毁了
}
int main(){
char *p;
p = fun();
printf("the p is %s\n",p);
return 0;
}
/*
宏定义:
用预处理指令#define声明一个常数,用以表明1年中有多少秒
c语言中:宏名:大写字母表示
*/
#include<stdio.h>
//#define SECOND_OF_YEAR (1234556)//这个写法有点杞人忧天
#define SECOND_OF_YEAR (365*24*3600UL) //这个UL要写在括号的内部
//常量表达式,编译的过程中计算机就能够算出来值了
//宏定义一定要注意括号的问题,需要使用括号进行保护。
int main(){
int a = SECOND_OF_YEAR;
printf("%d\n",a);
return 0;
}
数据申明
申请什么样的空间,以及如何使用
a)一个整型数
int a
b)一个指向整型数的指针
int *a;
c)一个指向指针的指针,它指向的指针是指向一个整型数
int **a;
d)一个有10个整型数的数组
int a[10];
e)一个有10个指针的数组,该指针是指向一个整型数的
int *a[10];
f)一个指向有10个整型数的数组的指针(一个指针,指向的是数组)
int (*a)[10]//这个时候先看左边的括号,则表明是一个指针,
int [10] *a =====> int *a [10]
g)一个指向函数的指针,该函数有一个整型参数并返回一个整型数
int (*a)(int) //函数名就是一个地址
h)一个有10个指针的数组,该指针指向一个函数。该函数有一个整型参数并返回一个整型参数
int (*a[10])(int)
*/
关键字static的作用是什么?
1、修饰局部变量
默认局部变量在栈空间存在,生存期比较短
加上static之后,叫做局部静态化,局部变量保存在静态数据段中保存(整个程序结束才结束,函数返回过后还在)
2、修饰全局变量
防止重命名,限制变量名只在本文件内起作用。函数名和变量名都是标签。
static int a;只在本文件内部起作用,对于外部的源文件不可见。
3、修饰全局函数
防止重命名,限制该函数只在本文件内起作用
关键字const的作用是什么?
C:只读,建议性,不具备强制性 != 常量
const int a = 100;//只用指针的形式可以修改
c++: 常量,C++编译器对const做了一些比较严格的定义
关键字volatile有什么含义?并给出三个不同的例子
防止C语言编译器的优化
它修饰的变量,该变量的修改只能通过第三方来修改。
当涉及到计数器、多线程和中断时,也就涉及到了第三方。
嵌入式系统系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a,
写两段代码,第一个设置a的bit 3,第二个清楚a的bit 3.在以上两个操作中,
要保证其他位不变。
unsigned int a;
a = a | (0x1<<3)
a = a & (~0x1<<3)
在某工程中,要求设置一绝对地址为0x67a9的整型变量为0xaa66.编译器是一个
纯粹的ANSI编译器。写代码去完成这一任务
int *p = (int *)0x67a9;
p[0] = 0xaa66; 或者*p = 0xaa66;
*((int *)0x67a9) = 0xaa66;
(
( void (*)(void) ) 0x67a9
)();
*/