printf("格式字符串", ...);
%i/%d int
%f/%lf float/double
%hd/%ld short int/long int
%u unsigned int
%c char
%s string
%p address
%o 八进制
%x/%X 十六进制
%g float/double去尾零
%e/%E 科学计数法
%% %
unsigned 无符号 格式:%u
signed 有符号 格式:%d
默认是有符号的,比如signed char ch;可以直接写成: char ch;
数据类型:
int
signed int ,有符号数最高位为符号位,0代表正数,1代表负数
最大的正数:01111111 11111111 11111111 11111111
最小的负数:10000000 00000000 00000000 00000000 ,负数以补码的形式存储
unsigned int
最大数: 11111111 11111111 11111111 11111111 , 最大的数加1就变为最小的数了
最小数: 00000000 00000000 00000000 00000000 , 最小的数减去1就变为最大的数了
32位操作系统:4字节,32位
16位操作系统中:2字节,16位
short int 短整型 格式:%hd
2字节,16位
无符号短整型 unsigned short int 格式:%hu
long int 格式:%ld
long long int 格式:%lld
unsigned long long int 格式:%llu
凡是int前面有修饰的类型 int可以省略不写, 如:unsigned int 可写成:unsigned
不能在float或double前加unsigned或signed
printf("%7.2f\n", f);//f总宽度是7,保留两位小数
C提供三种浮点类型
Float:单精度
Double:双精度
Long double :扩展双精度
C标准没有说明精度的范围有多少
IEEE提供了精度:
Float 1.17548*10(-38)~3.40282*10(38)
Double 2.22507*10(-308)~1.79769*10(308)
浮点常量
以下浮点常量有效
57.0 57. 57.0e0 57E0 5.7e1 5.7e+1 .57e2 570e-1
整数常量(字面值)
3被认为是int
3.5被认为是double
3.5f被认为是float
3.5L被认为是long double
35L被认为是long int
35LL被认为是long long
35u被认为是unsigned int
35UL是unsigned long
八进制和十六进制
0377
0x12ab
补码=反码+1
=========================================
sizeof(类型)
sizeof(变量名)
sizeof(表达式)
sizeof只关心类型,只会分析括号中的类型,不会对括号中的内容进行运算。如:
int i = 5;
sizeof(i=10)=4;
sizeof(i+3.14)=8;//3.14是double类型
+ - * / %
运算符/可能产生意外 的结果
运算符%要求操作数是整数
把0做为/或%的右操作数会出现未定义的行为
当运算符/和%用于操作负数,其结果未定义
C89中,操作数的其中之一是负数,结果可以向上取整,也可以向下取整。如-9/7的结果可以是-1,也可以是-2。
如果i和j其中一个是负数,i%j的结果依赖具体实现
C99中, 除法的结果总是向零截取,因此 -9/7的结果是-1,i%j的结果符号与i相同。
/**************************************************************************/
取余%
被除数为正数,余数为正数
被除数为负数,余数为负数
5 % -3 = 2
-5% -3 = 2
-5% 3 = -2
/**************************************************************************/
数组名之间不能赋值
数组名传递给函数时传递的是一个地址
数组作为参数时我们一般传递两个参数,数组名(数组首地址)和数组长度
void print(int x[], int n){
int i = n;
while(n--){
printf("%d\t", x[i-n-1]);
}
printf("\n");
}
//二维数组定义
void two_dimension_print(int x[][LINE], int len)
{
... ...
}
... ...
int array[2][4]={0,0};
two_dimension_print(array, 4);
... ...
函数存在隐式声明,它的返回值为int类型, 何为隐式声明,即不管你是什么类型它都指定为int类型
C99规则:
在调用一个函数之前,必须对其进行定义或声明
形式参数和实际参数的区别
void形参和空形参
函数调用 中的值传递
实际参数的转换
编译器在调用前遇到原型
编译器在调用前没有遇到原型
float --> double
char,short --> int
数组型参数
形式参数可以不用说明数组的长度
函数中无法确定数组长度,只能用第二个参数传入
形参是多维数组时,可以不指定数组的长度,但一定要指定数组的列数。
#include <stdlib.h>
void exit(int state);
exit(0);//退出整个函数
main函数中的rerurn与exit都是退出整个函数
scanf函数小技巧:
scanf("%*[^\n]");scanf("%*c");//这两个语句的作用是清空缓冲区。
局部变量
作用域:函数内部或者更小
生命周期:函数调用期间,函数执行时开始分配,函数调用结束时自动释放
局部优先:局部变量优先于全局变量(当两者重叠时),如:
... ...
int x=10;
void fa(int x)
{
printf("in fa x=%d\n", x);//打印的是局部变量x的值
int i=0;
for(; i<10; i++){
int x = i;
printf("in for(): x = %d", x);//打印的是此语句上定义的x的值
}
int main(){
fa(10);
return 0;
}
静态变量
作用域:函数内部或者更小
声明周期:整个函数运行期间,不受函数的限制
/**************************************************************************/
//函数返回指针的问题
... ...
... ...
int *fa(int *x){
(*x)++;
printf("in fa:x = %p\n", x);
return x; //ok
}
int *fb(void){
int x = 100;
printf("in fb:&x = %p\n", &x);
return &x; //不要返回局部变量的地址,函数返回时局部变量已经释放
}
int *fc(void){
static int si = 100;
return &si; //返回静态局部变量的地址是安全的
}
int main(void)
{
int a = 30;
int b = 10;
int *pa = fa(&a);
printf("pa = %p\n", pa);
int *pb = fb();
printf("pb = %p\n", pb);
int *pc = fc();
printf("pc = %p\n", pc);
printf("*pc = %d\n", *pc);
printf("\n");
return 0;
}
运行结果:
[root@localhost workspace]# ./pointer
in fa:x = 0xbffccbd4
pa = 0xbffccbd4
in fb:&x = 0xbffccba4
pb = 0xbffccba4
in fc:&si = 0x8049974
pc = 0x8049974
*pc = 100
//=============================================================
一个指针永远占4字节
sizeof(p) == 4;//p为指针
指针相减:(pointer1 - pointer)/sizeof(指针类型)
int x;
int* p1 = &x;
int* p2 = p1 +6;
p2 - p1 = 6;//两个指针相减并不是把它们的地址数值上的值相减
数组:int a[10] = {1,2,3,4,5}
在数值上:&a = a = &a[0]
但是不能:int *p = &a; //error
a[i] = *(a+i) = *(i+a) = i[a]
数组名的值是不可改变的:a++; //error,数组名是一个常指针
数组越界可能段错误,可能什么事都没有,可能破坏其他的数据
//==================================================================
字符串要点:
printf("%p\n", "abcdefg"); //打印的是存放该字符串的首地址
char c = "abcdefg"[1];//"abcdefg"返回该字符串的首地址
printf("c = %c", c); //c = 'b'
"abcedefg"[0] = 'A'; //error, 存放在代码区,字符串字面值不可修改
数组在栈区保存,字符串保存在代码区, 代码区是只读的
不可给数组名赋值
char str[]="aaa"; //字符串保存在栈区
str = "AAA"; // error, 数组名是常量,不能被修改,数组名不能出现在 '=' 左边
char *str2 = str; //ok
str[0] = 'A'; //ok
str = "abcdefg"; //ok
str[0] = 'A'; //error
字符串面量值保存在代码区,代码区只读,数组保存在栈区,可修改
一个指向字符串的指针能不能被修改,是看它指向的是代码区还是栈区,如果是栈区可修改,若是代码区则不能
//===========================================
scanf读不进空格,如:
... ...
char str[20];
scanf("%s", str);
printf("%s\n", str);
... ...
输入:abcdef ghig
输出:abcdef //原因是scanf读不进空格
printf("%10.6s\n", str);//打印字符串前面6个字符,字符串宽度为10,因为只打印前面6个字符,所以前面会补4个空格
输入
scanf(“%s”, str);//注意不需要加&
scanf永远不会读入空白符,而会跳过,换行、空格、制表等空白符会使scanf结束
gets()可以读取一行,而且不会在开始读字符之前跳过空白
gets()会持续读入直到换行符停止
gets()会忽略掉换行符
//=============================================
... ...
//以下代码存有风险
char *str3; //野指针
scanf("%s", str3);//有可能破坏数据,有可能段错误,有可能什么事都没有
printf("%s\n", str3);
/*************************************************/
/*************************************************/
const在指针上的用法
/*************************************************/
... ...
int a = 10;
const int *pa = &a; //pa所指向的空间不可以通过pa进行修改
//*pa = 30; //error
//=======================================
int b = 20;
int* const pb = &b;//pb本身的值不可被修改
//pb = &a; //error
*pb = 200;//pb所指向的空间可以被修改
//======================================
int c = 12;
const int* const pc = &c; //pc所指向的空间以及pc本身的值都不可被改变
//pc = &b; //error
//*pc = 300; //error
//======================================
const char *str1 = "abcdefg";
//str1[0] = 'A'; //error
str1 = "ABCDEFG";//ok
char* const str2 = "abcdef";
//str2 = "ABCDEF";//编译错误
//str2[0] = 'A';//段错误
const char* const str3 = "abcdef";
//str3 = "ABCDEF";//编译错误
//str3[0] = 'A'; //编译错误
char str[] = "abcdefg";
const char *str4 = str;
//str4[0] = 'A'; //编译错误
str4 = "ABCED"; //ok
char* const str5 = str;
str5[0] = 'A'; //ok
//str5 = "ABCDEF"; //error 编译错误
//字符串
char str[10] = "abcdefg";
strcpy(str, "abcdefghigklmalkdf", 10); //这是有问题的,因为'\0'无处放,所以当打印str时字符串后面可能出现乱七八糟的东西
strcpy(str, "abcdefghigklmalkdf", 9); //ok了
strlen(str);//不包括'\0'
char *ss[3] = {"aaaaa", "bbbfb", "ccccc"};//字符串存放在代码区
ss[0] = "AAAA";//OK
/*********************************************************************************
**********************************************************************************
*********************************************************************************/
宏定义:
不要在调用宏函数时传带 ++, --的参数
括起整个表达式,以防止宏函数参与运算时出现问题
括号括起变量,以防止传入参数时表达式出现问题
__LINE__ 被编译文件行号
__FILE__ 被编译文件名
__DATE__ 编译的日期
__TIME__ 编译的时间
__STDC__ 如果编译器是标准的,那么值为1
#运算符
只能出现在宏函数中,其作用是将参数字符串化
#define PTINT_INT(n) printf(#n “=%d\n”, n);
##运算符
将两个记号“粘合”在一起,成为一个记号,其中一个记号一般为宏参数
#define ID(n) i##n
int ID(1), ID(2), ID(3)
#define GEN_MAC(type) \
type type##_max(type x, type y) \
{ return x > y ? x : y;}
#ifdef IA32 等效于 #if defined (IA32)
#undef IA32 //#undef 是取消宏定义
结构体:
struct{
int id;
char name[10];
int onHand;
}part1,
part2 = {1, "part2", 100},
part3;
part1.id = 1;
strcpy(part1.name, "part1");
part1.onHand = 200;
part3 = part2;//这种赋值方式 ok
typedef struct Part Part;//起别名
int a[10];
typedef int IA10[10]; //起别名
IA10 b;
int i;
for(i=0; i<10; i++){
printf("%d\t", b[i]);
}
printf("\n");
/******************************************************************************/
结构的对齐与补齐:
由于内存分配会将结构中的变量分配到内存的边界上,以方便访问。
所以每个成员放的位置是从本身长度的倍数位开始放。但本身长度
超过4时,以4计。此称为对齐。
char 1倍
short 2倍
int 4倍
double 4倍
整个结构变量的长度要保持内部最长成员(超过4以4计)的倍数。如果不够,则补齐。
typedef struct Inner{
short s;
int i;
}Inner;
//ssXXiiii, 补齐
typedef struct Date{
char c;
short s;
int i;
}Date;
//cxssiiii, 对齐
typedef struct Date5{
int i;
char c;
}Date5;
//IIIIcxxx,补齐
typedef struct Date6{
double d;
char c;
}Date6;
//DDDDDDDDCxxx,补齐
typedef struct{
char a;
char b;
char c;
}Date7;
//ccc
/******************************************************************************/
用整个一个字节(char)去表示一个二进制变量(如一个on/off开关)看起来有点铺张浪费,
而char已经是c中的可以独立分配与寻址的最小单位了。此时可使用域
MS-DOS操作系统中存储日期的方式
struct file_date{
unsigned int day : 5;
unsigned int month:4;
unsigned int year : 7;
};
位域类型必须是int ,unsigned int或signed int,int 有些编译器上会有二义性。
不能取得域的地址。除此之外,域完全可以像其他变量一样使用。
int main(void)
{
typedef unsigned int UINT;
struct Ctl{
UINT INT : 1;
UINT C1 : 1;
UINT C2 : 1;
UINT C3 : 1;
UINT RES : 1;
}c;
c.INT = 0;
c.C1 = 1;
c.C2 = 1;
c.C3 = 0;
//c.RES = 2; //溢出
c.RES = 0;
//&c.C1; //不允许取地址
printf("sizeof(struct Ctl) = %d\n", sizeof(struct Ctl));//输出4
printf("\n");
return 0;
}