%d,有符号整型数,字节数看机器,本机4字节
printf("%d\n",0x7fffffff);//2147483647 2e31-1 32位 4字节
printf("%d\n",0xffffffff);//-1 最高位是符号位
此外,
%ld long类型
%lld longlong类型
宽度和精度可以用 * 表示,如:
printf("%.*s", max, s);
其中max为int类型,表示从字符串s中打印最多max个字符。
printf("%m.nf",xxx)
表示**一共最小**m列,小数点后面n位。
.
表示之前一共最小是之前的那个整数,不够则用空格补上,-
是右边补空格。多了则无影响。
printf返回值为打印的字符数。
printf使用第一个参数判断后面参数的个数及类型。
需注意:
printf(s); /* FAILS if s contains % */
printf("%s", s); /* SAFE */
sizeof(argv)/sizeof(char*))
include
main()
{
char buffer[100];
printf("%s",getcwd(buffer,100));
}
p1 = (char *)malloc(10);
p2 = new char;
char str[20]="0123456789"
int aa=strlen(str);
int bb=sizeof str;
printf("aa=%d\n",aa); //aa=10
printf("bb=%d\n",bb); //bb=20
char str1[]="0123456789"
int cc=sizeof str1;
printf("cc=%d\n",cc); //bb=11,最后多个'\0'
int array[20]={1,2,3,4};
int ai=strlen(array); //实质上没有意义,但可以看看结果
int bi=sizeof array; //可以不加括号,因为sizeof不是函数,而是编译器提供的操作符
printf("ai=%d\n",ai); //ai=1,1的第二个字节为0,等于字符串结束符'\0'
printf("bi=%d\n",bi); //bi=80,内存总共分配的字节数
printf("size=%d\n",bi/sizeof(array[0]));//数组个数size=20
char *p = "how are you doing?";
printf("p=%d\n",p); // p=4210824,首元素'h'地址
printf("*p=%c\n",*p);//*p=h,首元素'h'值
char ap[] = "how are you doing?";
*(ap+1)='w';//或者 ap[1]='w';
printf("ap[1]=%c\n",ap[1]);// ap[1]=w,修改成功
char *p = "how are you doing?";
*(p+1)='w'; //运行到此停住了,修改不了
printf("*(p+1)=%c\n",*(p+1));
第一种方式:
"how are you doing?"
保存在栈空间数组里,数组名为ap,函数名为数组的首地址。可以通过*(ap+1)='w';或者 ap[1]='w';
的形式来修改数组内容。
第二种方式:
char *p = "how are you doing?";
"how are you doing?"
保存在文字常量区,该数据不能修改,默认有只读属性。由指针p指向,不能通过指针p来修改此值。
//无效的swap函数
void swap(int a, int b)
{
int temp = a;
a = b;
b = temp;
}
//有效的swap函数
void swap_p(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
32位机4字节;64位机8字节
char ap[] = "how";
printf("%d\n",sizeof(ap)); //4,数组大小
printf("%d\n",sizeof(&ap[0])); //8,指针大小,获取第一位指针,不能直接用ap来获取指针大小
printf("%d\n",sizeof(ap+1)); //8,指针大小
char arr[][3] = {{1,2,3},{4,5,6}};
printf("arr=%d\n",arr); //10485120,arr指向首元素,即arr[0],此处为arr[0]地址
printf("*arr=%d\n",*arr); //10485120,arr指向首元素,即arr[0],*arr取出其指向的元素,即arr[0]的值
printf("*arr+1=%d\n",*arr+1); //10485121,arr指向首元素,即arr[0],*arr取出其指向的元素,即arr[0]的值,arr[0]的值也是指针,指向1字节数据,故*arr存储单位为1字节,下一个对象的地址+1
printf("&arr[0][0]+1=%d\n",&arr[0][0]+1);//10485121,整个arr[0][0]为整体(存储单位:1字节),下一个对象的地址+1
printf("&arr[0]+1=%d\n",&arr[0]+1); //10485123,整个arr[0]为整体(存储单位:3字节),下一个对象的地址+3
printf("arr+1=%d\n",arr+1); //10485123,arr指向首元素,即arr[0](存储单位:3字节),下一个对象的地址+3
printf("&arr+1=%d\n",&arr+1); //10485126,整个arr为整体(存储单位:6字节),下一个对象的地址+6
printf("**arr=%d\n",**arr); // 1
printf("*(*arr+1)=%d\n",*(*arr+1)); // 2
printf("**(arr+1)=%d\n",**(arr+1)); // 4
类似于* 和 ++ – 这样的一元运算符遵循从右至左的结合顺序。(K&R. P80)
++*p
等价于(*p)++
int a[][3] = {{1,2,3},{4,5,6}};//a是二维数组,内存中连续排列
int (*p)[3] = a;//p是数组指针,指向int[3]类型数据(int为3时,共12字节)
printf("%d\n",sizeof(*p));// 12
printf("%d\n",p); // ....296,首地址
printf("%d\n",*p);// ....296,首地址
printf("%d\n",sizeof(*(p+1)));// 12
printf("%d\n",p+1);//....308,加上sizeof(*p)的大小
printf("%d\n",sizeof(*(p+2)));// 12,不管该处地址是否真的有数据,进行加减法时统一乘上指针指向数据大小(12)
printf("%d\n",(*p)[2]); // 3
printf("%d\n",(*(p+1))[2]);// 6,注意括号,因为[]优先级高于*
再来看看指针数组:
int a[][3] = {{1,2,3},{4,5,6}};//a是二维数组,内存中连续排列
int *q[3] = {&a[0][0],&a[0][1],&a[0][2]};//等价于 int *(q[3]); q是指针数组,每个指针指向int型数据
printf("%d\n",q); // ....264
printf("%d\n",sizeof q); // 24=3*8(每个指针大小为8),
printf("%d\n",q[0]); // ...296,存1的地址
printf("%d\n",q[1]); // ...300,存2的地址
printf("%d\n",*(q[2])); // 3
printf("%d\n",**(q+2)); // 3
264+24=288,与296中间隔了8字节,不知道是啥。跳过中间8字节,可以直接访问到a数组(虽然有点无厘头…):
printf("%d\n",sizeof *(q+4)); //8
printf("%d\n",*(q+4)); //1,访问到了
printf("%d\n",*(q+5)); //3,加一以8字节为单位,故跳过了2
printf("%ld\n",*(q+5)); //还是3,原因是本机long也是4字节,故也只读了4个
printf("%d\n",*(*(q+2)+1)); // 4
printf("%d\n",*(q+2)+1); //....308
}
那怎么访问到2?
强制转换!!!
由于本机long也是4字节,故先将指针提取到的值转换成long long类型(8字节),再右移4字节,提取到高4位,也就是2。
printf("%d\n",((long long)*(q+4))>>32); //2
由于q是指针数组,即*q是指针
,故*(q+4)
也是指针,虽然我们知道q+4后指向的并不是指针,但在编译时编译器不知道,会报错:
error: invalid operands to binary >> (have ‘int *’ and ‘int’)
故需要强制转换成long long类型通过编译器检查,好进行非法的内存访问…
#include
int f1(void)
{
printf("f1 was called!\n");
return 1;
}
int* f2(void)
{
int i = 1;
printf("f2 was called!\n");
return &i;
}
main()
{
int f1(void); //函数指针
int (*p)(void);//p为 指向返回值为int的函数 的指针
p = f1;//f1是函数名,表示函数在内存中的地址
p(); //调用函数
int *f2(void);//指针函数,f2为 返回值为指向int型数据的指针 的函数
f2();//调用函数
}
为一种数据类型定义一个新名字。这里的数据类型包括内部数据类型(int,char等)和自定义的数据类型(struct等)。详见:百度百科
#include
main()
{
struct point{
int x;
int y;
};
struct point p1 = {1,2};
struct {
int x;
int y;
} p2 = {2,3};
printf("p1:x=%d,y=%d\n",p1.x,p1.y);
printf("p2:x=%d,y=%d\n",p2.x,p2.y);
}
point 是 结构标记,等价于花括号内内容(含花括号)。
struct声明定义了一种数据类型,故也可以用typedef为此类型起别名:
typedef struct point{
int x;
int y;
} Point;
Point p3 = {3,4};
printf("p3:x=%d,y=%d\n",p3.x,p3.y);
此处类型struct point
等价于Point
。
C中没有像java中那样的String类型,可以自己定义,如下是几种定义方式,分别使用了typedef和define
//typedef 对结构类型重命名
typedef struct String{
char *value;
} String1;
String1 str1 = {"string"};
printf("%s\n",str.value);
//typedef 对字符指针类型重命名
typedef char* String2;
String2 str2 = {"string"};
printf("%s\n",str2);
//define 给文本段起别名,编译时替换(较高级的替换,若带参数也会替换)
#define String3 char*
...
String3 str3 = "string";
printf("%s\n",str3);
先定义结构实例origin及指向origin的指针p,并初始化实例内成员值。
struct {
int x;
char *str;
} origin,*p=&origin;
//两种指针访问结构内部成员方式
(*p).x=1;
p->str="string";
//x=1, str=string
printf("x=%d, str=%s\n",p->x,p->str);
printf("x=%d, first_str=%c\n",p->x,*p->str++);//x=1, str=s
printf("x=%d, str=%s\n",p->x,p->str);//str=tring
第一个printf:先读取指针str指向的字符,再对str进行++,而不是读到的字符;
第二个printf:str++后指向的首字符为t,以字符串输出(后移输出直至’\0’)。
printf("x=%d, str=%c\n",p->x,(*p->str)++);
printf("x=%d, str=%s\n",p->x,p->str);
运行后程序无反应,说明有问题。
第一个printf里,++对象是str指向的字符串,该字符串存于常量区,故不能进行修改操作,++操作不能进行;
让str指向可以修改的字符串就行了:
char string[] = "string";//栈区分配内存来创建字符数组
p->str=string;//指向栈区的字符数组,就能进行修改了。
输出结果为:
x=1, first_char=s
x=1, str=ttring
printf("x=%d, first_char=%c\n",p->x,*p++->str);//x=0, str=s
printf("x=%d, str=%s\n",p->x,p->str);//x=0, str=
先读取指针str指向的对象的值,*p++->str
语句结束后p执行+1,故p->x为0(未定义值)。后面的输出也都是没有定义的值。
猜测是最大的数据类型所占内存。
#include
union var{
long long l;//本机8字节
int i;
}
main(){
union var v;
v.i = 5;
printf("v size is %d\n",sizeof v);//8
printf("v.l is %d\n",v.l);//5
}
机器字:指计算机进行一次整数运算所能处理的二进制数据的位数。
一个位域必须存储在同一个机器字中,不能跨两个机器字。如一个机器字所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如:
#include
struct {
unsigned int is_a :1;
unsigned int is_b :20;
unsigned int is_c :20;
} flags;
main(){
printf("v size is %d\n",sizeof flags);
}
结果是8,is_c无法存放于32-21=9位中,故存于另一个机器字(本机一个机器字为32位)中。
#include
struct {
unsigned int is_a :1;
unsigned int :0;
unsigned int is_b :20;
unsigned int is_c :20;
} flags;
main(){
printf("v size is %d\n",sizeof flags);
}
结果是12,这里用无名字段特殊字符0强制在下一个机器字边界上对齐。
字符读取来源可以不是从键盘,也可以通过:
1. 文件,如下:
test.exe < lines.txt
此时,
#include
main()
{
int c;
while((c = getchar())!=EOF)
printf("%c",c);
}
#include
main()
{
printf("output from another program");
}
test.c:
#include
main()
{
int c;
printf("it's ");
while((c = getchar())!=EOF)
printf("%c",c);
}
运行:from.exe | test.exe
得到:
it's output from another program
from.exe的输出没有直接出来,而是通过管道重定向给了test.exe。
转换方式和printf一样,但将输出保存到一个字符串中。
int sprintf(char *string, char *format, arg1, arg2, ...);
标准头文件
中包含一组宏定义,他们对如何遍历参数表进行了定义。va_list ap 将使ap依次引用各参数参数。
va_start用于初始化ap,使其指向首个无名参数。
调用va_arg,返回ap指向的一个指定类型的参数,并将ap指向下一个参数。
#include
#include
void minprintf(char *fmt,...)
{
va_list ap;
char *p, *sval;
int ival;
double dval;
va_start(ap, fmt);
for (p = fmt; *p; p++){
if (*p != '%'){
putchar(*p);
continue;
}
switch(*++p){
case 'd':
ival = va_arg(ap, int);
printf("%d",ival);
break;
case 'f':
dval = va_arg(ap, double);
printf("%f", dval);
break;
case 's':
for (sval = va_arg(ap, char *); *sval; sval++)
putchar(*sval);
break;
default:
putchar(*p);
break;
}
}
va_end(ap);
}
main()
{
void minprintf(char* fmt,...);
minprintf("%d,%s",44,"string");
}
与printf(格式化输出对应)
参数必须是指针。
返回值为成功匹配并赋值的输入项个数。
#include
main()
{
double sum, v;
sum = 0;
while (scanf("%lf", &v) == 1)//表示将输入的字符串(以'\t','\n',' '等作为字符串结束标志)以double形式保存到地址&v中
printf("\t%.2lf\n", sum += v);
}
sscanf用于从一个字符串中读取字符序列,而不是键盘或其他方式:
int sscanf(char *string, char *format, arg1, arg2, ...)
可以用于检测某个string是否符合某个标准格式。
fopen若文件读取失败,返回NULL
1. putc 和 getc(文件的单字符输入输出)
int getc(FILE *fp)
从文件返回下一个字符。
int putc(int c, FILE *fp)
将c写入文件,返回写入的字符。
以下将read.txt文件内容转换为大写后保存到write.txt中
#include
#include
main()
{
FILE *fp_r;
FILE *fp_w;
fp_r = fopen("read.txt","r");
fp_w = fopen("write.txt","w");
int c;
while((c=getc(fp_r))!=EOF){
printf("%c",c);
putc(toupper(c),fp_w);
}
}
int fscanf(FILE *fp, char *fmt, ...)
若为文件末尾,返回EOF int fprintf(FILE *fp, char *fmt, ...)
#include
main()
{
FILE *fp_r;
FILE *fp_w;
fp_r = fopen("read.txt","r");
fp_w = fopen("write.txt","w");
char *str;
char *c;
while((c=fgets(str,1000,fp_r))!=NULL){
printf("%s",str);
fputs(str,fp_w);
}
}
函数 | 描述 | 正常返回值 | 异常返回值 |
---|---|---|---|
fputs | 将字符串写入文件 | 非负值 | EOF |
fgets | 从文件读取一行到字符数组中 | 字符数组指针 | NULL |
ferror | 文件流是否出错 | 否:0 | 是:非0值 |
foef | 文件流是否达到结尾 | 否:0 | 是:非0值 |
printf("%s",strpbrk("qqqwer","w"));
得到wer指定操作长度的函数:
从字符串B拷贝指定长度字符到字符串A:memmove
#include
#include
main()
{
char p[]="qqqwer";
printf("%s",(char *)memmove(p,"w",1));//wqqwer
}
注:字符串A不能是位于常量区不可修改的字符串
比较前n个字符:memcmp
char *strtok(char *s, const char *delim)
s为要分解的字符,delim为分隔符字符(如果传入字符串,则传入的字符串中每个字符均为分割符)。首次调用时,s指向要分解的字符串,之后再次调用要把s设成NULL。
注:原来的string会变掉。
#include
#include
main()
{
char str[100] = "abc,d,e|f";
char *sep = ",|";
char *p = strtok(str,sep);
while(p != NULL){
printf("%s\n",p);
p = strtok(NULL,sep);
}
printf("final str is %s",str);
}
字符串转换成其他类型
随机数生成
内存分配
其他
system("echo hhh");
printf("%s",getenv("classpath"));
qsort:快排
void qsort(void *base,int n,int size,int (*cmp)(const void *,const void *));
bsearch:二分查找
void *bsearch(const void *key, const void *base, size_t nmem, size_t size, int (*cmp)(const void *, const void *));
第一个参数必须是某个待比较值的指针,不能是某个待比较值。
#include
#include
int cmp(const void *a, void *b){
return *(int *)a-*(int *)b;
}
main()
{
int arr[] = {3,5,4,2,1};
qsort(arr, sizeof(arr)/sizeof(int), sizeof(int), cmp);
int i;
for (i=0; i<5; i++)
printf("%d ",arr[i]);
int *res,key = 3;
res = (int*)bsearch(&key,arr,5,sizeof(int),cmp);
printf("\n二分查找3结果为:%d",res==NULL?-1:*res);
key = 7;
res = (int*)bsearch(&key,arr,5,sizeof(int),cmp);
printf("\n二分查找7结果为:%d",res==NULL?-1:*res);
}
1 2 3 4 5
二分查找3结果为:3
二分查找7结果为:-1
复杂结构的排序:
#include
#include
#include
typedef struct
{
char name[6];
int score;
}Student;
int cmp_name(const void *a, void *b){
return strcmp(((Student *)a)->name,((Student *)b)->name);
}
int cmp_score(const void *a, void *b){
return ((Student *)a)->score-((Student *)b)->score;
}
main()
{
Student s[5] =
{
"Zhang", 94,
"Wu",89,
"Wei",88,
"Li",99,
"Fang",90
};
int i;
printf("sort by name:\n");
qsort(s, 5, sizeof(Student), cmp_name);
for (i=0; i<5; i++)
printf("%-6s %d\n",s[i].name,s[i].score);
printf("\nsort by score:\n");
qsort(s, 5, sizeof(Student), cmp_score);
for (i=0; i<5; i++)
printf("%-6s %d\n",s[i].name,s[i].score);
}
sort by name:
Fang 90
Li 99
Wei 88
Wu 89
Zhang 94
sort by score:
Wei 88
Wu 89
Fang 90
Zhang 94
Li 99