代码需要注释 撰写说明 撰写时间 撰写人等
ojh@ojh-virtual-machine:~/v1_tast$ file ptr_val
ptr_val: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=57d9623c9ff60595d91ec0d310d969b08584c8dd, not stripped
ELF:可执行文件
64-bit:64位
LSB:小端字节序 (地位在低地址空间)
x86-64:x86架构下的64位
dynamically linked:动态库
not stripped:没有调试信息
多个c文件的原则
一个功能一个函数
一个模块一个c文件
一个c文件一个头文件
c文件放定义,头文件放声明
c1-学习日志
字符
字符与字符串
- char ch=‘a’;
- char ch=‘0’->ASCII 0x30
char *str=“hello” str指向首地址,以\0结尾
输入类型(全缓冲 行缓冲 不带缓冲 )
重定向:标准输入 标准输出 标准出错
字符标准输入 getchar()
字符标准输出 putchar()
字符串标准输入 gets()
字符串标准输出 puts()
字符串函数
字符串长度 strlen 不包含\0
占用的存储空间 sizeof 包括’\0’
连接连个字符串strcat <–>strncat--------------strncat(str1,str2,szeof(str1))
字符串区分大小写比较strcmp <–> strncmp--------------strncmp(str1,str2,n)比较前n个字符
字符串忽略大小写比较strncasecmp <–> strncasecmp
字符串拷贝strcpy <–> strncpy---------------strncpy(str1,str2,n)拷贝前n个字符
格式化输出到buffer sprintf<–>snprintf
字符串匹配子串strstr-------返回真或假
字符串中找到某个字符 strchr<–>strrchr
字符串切割 strtok
字符串初始化输入sscanf
设置一片内存的值memset---------memset(arr,0,sizeof(arr))
内存拷贝memcpy----------memcpy(arr1,arr2,100*sizeof(int))
内存剪切(内存重叠)memmove
内存的比较memcmp
文件输入和输出
fp=fopen()
fetc() fscanf() fgets() fread()
putc() fprintf() fputs() fwrite()
fseek() ftell() fflush() setvbuf()
fclose()
文件结尾 EOF <stdio.h>
指针
**p 指向指针的指针
char ch=‘a’;
char *p=&ch;
char *str=“hello”;
char **p=&str;
#include<stdio.h>
int main (int argc, char **argv)
{
int a =0x12345678;
int *p1 = NULL;
char *p2 = NULL;
p1 = &a;
p2 =(char *)&a;
printf("p1 address: %p p1 value: %p p1 point memory value: 0x%x\n", &p1, p1, *p1 );
printf("p2 address: %p p2 value: %p p2 point memory value: 0x%x\n", &p2, p2, *p2 );
p2++;
p1++;
printf("p1 address: %p p1 value: %p p1 point memory value0x%x\n", &p1, p1, *p1 );
return 0;
}
ojh@ojh-virtual-machine:~/c1_tast$ ./strprint
p1 address: 0x7ffdff888848 p1 value: 0x7ffdff888844 p1 point memory value: 0x12345678
p2 address: 0x7ffdff888850 p2 value: 0x7ffdff888844 p2 point memory value: 0x78
p1 address: 0x7ffdff888848 p1 value: 0x7ffdff888848 p1 point memory value0xff888848
数据交换
#include<stdio.h>
void swap(int *a, int *b)
{
int tmp;
tmp = *a;
*a = *b;
*b = tmp;
}
int main(void)
{
int a=10;
int b=20;
printf("a=%d b=%d\n", a, b);
swap(&a, &b);
printf("a=%d b=%d\n", a, b);
}
const 关键字
const char *p ------------ const 修饰 *p
char * const p------------const 修饰 p
const char * const p----const 修饰 *p与p
内存布局
- 系统空间:每个程序运行独立4GB虚拟内存空间
- 命令行传递的参数: int main(int argc,char **argv)
- 栈区:局部变量(自动分配,·内有效,离开0自动)
- 堆区: malloc分配的内存(自己管理,用完free,否则泄漏)
- .bss:未初始化的全局变量或static变量
- .data:初始化过的全局变量或static变量
- .rodata:const, #define,char *ptr="string"等定义的数据常量
编译出来的可执行文件的二进制代码
复合数据类型
联合union
联合是一个能在同一个存储空间里(但不同时)存储不同类型数据的数据类型
union u_data
{
unsinged cahr a;
unsinged int b;
}data;
sizeof(data)=4
data.b=0x12345678
data.a=0x78---------(小端字节序)
data.a=0xef
data.b=0x123456ef
可以应用联合判断大小端字节序
枚举
可以使用枚举类型声明表示整数常量的符号名称
enum
{
}
结构体
struct tag {
member-list
member-list
member-list
...
} variable-list ;
菜鸟教程
tag 是结构体标签。 member-list 是标准的变量定义,比如 int i; 或者 float f;,或者其他有效的变量定义。
variable-list 结构变量,定义在结构的末尾,最后一个分号之前,您可以指定一个或多个结构变量。
enum {male,female};
struct st_student //不分配内存空间
{
char name[32];
int gender;
int age;
float score;
};
struct st_student student = {"zhangsan", male, 20, 90.0}; //按顺序存放
struct st_student student ={.name="zhangsan",.gender=male, .age=20, .score=90.0};
printf("Name:%s Gender:%d Age:%d Score: %f\n",student.name, student.gender, student.age, student.score);
结构体对齐
typedef struct _st_struct1
char a;
shor b;
tin c;
}st_struct1;
sizeof(st struct1) = ?
- 自身对齐值: 自身对齐值就是结构体变量里每个成员的自身大小;
- 指定对齐值: 指定对齐值是由宏#pragma pack(N)指定的值,里面的N必须是2的幂次方如1,2,4,8,16等。如果没有通过#pragma pack宏那么在32位的Linux主机上默认指定对齐值为4,64位的Linux主机上默认指定对齐值为8,ARM CPU默认指定对齐值为8:
- 有效对齐值: 结构体成员自身对齐时有效对齐值为自身对齐值与指定对齐值中较小的一个,结构体圆整时,为所有成员中自身对齐值最大的与指定对齐值较小的一个;
Typedef
Typedef用来定义一种新的数据
typedef unsigned char u8;
typedef unsigned short uint_16;
u8 var1, var2;
uint_16 var1, var2;
typedef char *pchar;
typedef int *pint;
pchar var1, var2;
pint var1, var2;
#define PCHAR char *
PCHAR var1,var2;
ps: char * p1,p2; //*p1为char ×类型,而p2为char类型
可以将typedef用在结构体上
typedef struct st_studebt
{
…
}st_student;
结构体嵌套
typedef struct _st_score
{
float math;
int english;
int chinese;
}st_score;
typedef struct _student
{
char name[32];
int gender;
int age;
st_score score;
st_score *pscore;
}st student;
st_students var,*p=&var;
var.score.chinese;
var.pscore->chinese;
p->score.chinese;
(*p).score.chinese;
p->pscore.chinese;
结构体传参
st_student st;
increase_age(&st);
increase age(st student *p)
{
P->age++;
}
函数指针与回调函数
位操作与宏
位运算符
左移运算<<-----0x8a:1000 1010 <<2 左移2位 0x28:0010 1000
右移运算>>-----0x8a:1000 1010 >>2 右移2位 0x22:0010 0010
m<<n: m*n2^n
m>>n: m/n2^n
位操作
把第三位设为1: var | 1<<3------或
把第n位设为1: var | 1<<n-------或
把第三位设为0: var & (~(1<<3))------与 取反
把第n位设为0: var & (~(1<<n))------与 取反
把第三位反转: var ^ 1<<3 -------异或
获取第三位的值:(var>>3) & 0x1
把6到3位设为1:var | 0xf<<3 ---- 或
把6到3位设为0:var & ~(0xf<<3) ---- 与取反
位字段
位字段(bit field)是一个signed int或unsigned int中一组相邻的位。位字段由一个结构体声明建立,该结构体声明为每个字段提供标签,并决定字段的宽度,例如:
strut gpio_bit
{
unsinged int gpv0:2;
unsinged int gpv1:2;
unsinged int gpv2:3;
unsinged int gpv3:3;
unsinged int gpv4:2;
}
如果声明的总数超过了一个unsigned int大小,那么会使用下一个unsigned int的存储位置。这时不允许一个字段跨越两个unsigned int之间的边界。
宏
#defineMAX_STUDENTS 10
#defineADD(a, b) a + b
#defineMUL(a,b) a * b
直接替换
var =5*ADD(3,4) =5 * 3+4
var=MUL(3+4,g)=3+4 * b
修改为带括号
#defineADD(a, b) ((a) + (b))
#defineMUL(a,b) ((a) * (b))
宏#与##(粘合剂)
- 一个#
#define PRINT SQR(x) printf(“The square of”#x" is %d.n", ((x) * (x))
#define PRINT SQR2(x) printf(“The square of x is%d.n”,((x) * (x))
int y = 5;
PRINT_SQR2(y); // The square of x is 25
PRINT_SQR(y);// The square of y is 25PRINT SQR(2+4); // The square of 2+4 is 36
- 两个#
#define VAREXT(x) var##x
var1= 10;
var2 = 20;
printf(“%d”,VAREXT(1));
输出结果为10
条件编译/ 预处理器
指令 | 描述 |
---|---|
#define | 定义宏 |
#include | 包含一个源代码文件 |
#undef | 取消已定义的宏 |
#ifdef | 如果宏已经定义,则返回真 |
#ifndef | 如果宏没有定义,则返回真 |
#if | 如果给定条件为真,则编译下面代码 |
#else | #if 的替代方案 |
#elif | 如果前面的 #if 给定条件不为真,当前条件为真,则编译下面代码 |
#endif | 结束一个 #if……#else 条件编译块 |
#error | 当遇到标准错误时,输出错误消息 |
#pragma | 使用标准化方法,向编译器发布特殊的命令到编译器中 |
linux下只做预处理
gcc -E X.c -o X.i
注释代码
#if 0
需要注释的代码
#endif
头文件
#include<stdio.h>与#include "stdio.h"区别
前者:找头文件会在系统路径下查找(linux下头文件 urs/include)
后者:先在当前路径下查找,如果找不到则在系统路径下查找头文件
- 头文件书写规范——防止重复包含宏
led.h
#ifndef __LED_H
#define __LED_H
……
#endif
常用调试宏
可变参数宏
volatile 关键字
olatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。
用宏对寄存器进行读写操作
- 写入寄存器
#define write_reg(addr,var) *(volatile unsinged int *)(addr)=(var) - 读取寄存器数据
#define read_reg(addr) *(volatile unsigned int *)(addr)