使用教材:《C语言程序设计》K.N.King
- printf("%05.2f",1.1),中05.2表示一共显示10位,0代表不足的用0补齐,2在.后面表示小数点后面保留两位,若在05.2前面加上符号则表示右边填充,不加默认左边填充
- scanf在寻找起始位置时,会忽略空格、制表符、换行符等,但不会忽略非空字符,如:scanf("%d%d")与scanf("%d,%d"),如果输入为1换行2,则第一个可以读入数据,第二个无法读入,因为第一个字符后面需要有一个逗号
但当读取的是字符时不会忽略空格 - int类型除以int类型还是int类型
- scanf勿忘变量前面的“&”
- 千万不要把int类型赋值为float类型,这样会输出0.000000
- 当除法运算符与取余运算符用于负的操作数时,输出结果与具体代码运行的硬件平台有关
- 运算符等于号是右结合的,如下式最终结果j=33.0
int i;
float j;
j = i = 33.3;
- 复合赋值运算是右结合的
i += j += k;
/*等价于*/
i += (j += k);
- 想要在代码中使用“true”或者“false”的话,需要在一下三行代码
#define BOOL int /*定义一个BOOL类型,本质是int类型*/
#define TRUE 1 /*定义TRUE代表的是整数1*/
#define FALSE 0 /*定义FALSE代表的是整数0*/
- 在读取与输出时
scanf("%u",&a) /*无符号数*/
scanf("%o",&a) /*基数为八进制*/
scanf("%x",&a) /*基数为十六进制*/
scanf("%hd",&a)/*短整型*/
scanf("%ld",&a)/*长整型*/
/*
printf函数格式与scanf一样
*/
- 读取字符与输出字符尽量用getchar()与putchar()
i = (long int) j * j
与i = (long int)(j * j)
两者的区别是,前者强制转换在数值溢出之前,后者数值溢出在强制转换之前- 字符串数组
char *yourArrName[]
,输出该数组元素使用printf("%s",arr[i])
- 不要随便乱加空格!!!!
- 声明指针类型变量时,以下两种形式都被接受,星号可以紧贴int类型也可以紧贴变量名,以整型变量地址指针为例
int* p1=&a;
int *p2=&a;
- 指针操作的小例子
#include <stdio.h>
static int pi=1;
int main(){
int a[10]={0,1,2,3,4,5,6,7,8,9};
for(int i=0;i<sizeof(a)/sizeof(int);i++){
printf("第%d个元素的地址为:%d,该元素值为:%d\n",i,&a[i],*&a[i]);
}
printf("数组a的地址为:%d\n",&a);
printf("数组a的地址偏移加一为:%d\n",&a+1);
int b=1;
printf("数b的地址为:%d\n",&b);
printf("数b的地址偏移加一为:%d\n",&b+1);
int* p1=&a;
int *p2=&a;
printf("%d %d\n",p1,p2);
printf("%d %d\n",pi,&pi);
}
运行结果:
第0个元素的地址为:6356684,该元素值为:0
第1个元素的地址为:6356688,该元素值为:1
第2个元素的地址为:6356692,该元素值为:2
第3个元素的地址为:6356696,该元素值为:3
第4个元素的地址为:6356700,该元素值为:4
第5个元素的地址为:6356704,该元素值为:5
第6个元素的地址为:6356708,该元素值为:6
第7个元素的地址为:6356712,该元素值为:7
第8个元素的地址为:6356716,该元素值为:8
第9个元素的地址为:6356720,该元素值为:9
数组a的地址为:6356684
数组a的地址偏移加一为:6356724
数b的地址为:6356680
数b的地址偏移加一为:6356684
6356684 6356684
1 4202496
- 若p为指针
表达式 | 含义 |
---|---|
*p++或*(p++) | 自增前的表达式是*p,然后自增p |
(*p)++ | 自增前的表达式是*p,然后自增*p |
*++p或* (++p) | 先自增p,自增后的表达式*p |
++*p或++(*p) | 先自增*p,自增后的表达式*p |
- 可以将数组名作为数组第一个元素的地址,如以下两段代码等价
注意:当数组名用作地址时,不可改变,但将该地址赋给一个指针,然后改变该指针的值是合法的
int arr[10];
int sum=0;
for(int* p=&a[0];p<&a[10];p++)
sum+=*p;
int arr[10];
int sum=0;
for(int* p=a;p<a+10;p++)
sum+=*p;
- 数组反序指针版
#include <stdio.h>
int main(){
int arr[10]={0,1,2,3,4,5,6,7,8,9};
for(int* p=arr;p<arr+5;p++){
int temp=*p;
*p=*(arr+9-(p-arr));
*(arr+9-(p-arr))=temp;
}
for(int* p=arr;p<arr+10;p++) printf("%d %d\n",p,*p);
return 0;
}
- 二维数组arr,指针arr[i]代表数组arr第i行的第一个元素的地址
- 二维数组arr,当用arr作为指针时,该指针是二级指针,该二级指针指向arr[0],而arr[0]是一级指针,所以arr是指向整数的指针(arr[0])的指针。
arr(二级指针)————》arr[0](一级指针)————》arr[0][0](整数) - 指向数组的指针才能进行运算,指向整型或者字符的指针都不能进行运算
- 用来放置字符串的字符数组一定要确保初始长度大于!!不是大于等于是大于!!字符串长度,为了留给位置给空字符,若没有空字符数组将无法作为字符串使用。初始化字符数组时可以忽略初始长度,让编译器自行判断
char str[]="hello world!"
,也可以使用char* str="hello world!"
,指针的声明方式与数组的声明方式都可以更改字符串内元素,注意指针是数组的索引,所以以下代码str数组与指针p指向的数组是同一个数组,指针修改数组元素需要使用*(p+i)=某值
的形式
#include <stdio.h>
int main(){
char str[]="abcdefg";
char* p=str;
puts(str);
puts(p);
str[0]='A';
*(p+1)='A';
puts(str);
puts(p);
return 0;
}
abcdefg
abcdefg
AAcdefg
AAcdefg
- scanf在读取字符串的时候会忽略前面的空字符,而gets函数不会忽略,scanf遇到空字符就会停止,而gets函数直到读到换行符才停止。且scanf会将换行符留给下次操作,而gets函数会读取换行符并将其转化为‘\0’存到字符串内。当用户输入“a b c d”时,scanf第一次只会读取“a”,下一次scanf从a后面的空格开始,而gets会读取整个字符串,但这两种函数都有越界存储的可能性,所以并不安全
- 字符串数组(本质是二维数组),最好的实现形式是以字符数组指针作为元素组成一个指针数组。但该数组的长度无法通过某些函数自动获取,必须是已知量。
#include <stdio.h>
int main(){
char* strArr[]={"温斯顿","查莉娅","麦克雷","小美","8D","布里吉塔"};
for(int i=0; i<6; i++)
printf("%s\n",strArr[i]);
return 0;
}
结果
温斯顿
查莉娅
麦克雷
小美
8D
布里吉塔
- 在使用带参数的宏定义时,当引用参数时必须加括号不然会造成问题,如
#include <stdio.h>
#define newOpt(n,m) (n*m-3)
int main(){
printf("%d\n",newOpt(2-1,3));
return 0;
}
/*此种情况结果为:-4,即newOpt函数将表达式看作2-1*3-3,所以结果是-4*/
#include <stdio.h>
#define newOpt(n,m) ((n)*(m)-3)
int main(){
printf("%d\n",newOpt(2-1,3));
return 0;
}
/*此种情况结果为:0,newOpt函数将表达式看作(2-1)*3-3,所以结果为0*/
- malloc函数,若参数为整数,如malloc(5),则是分配5个字节的空间
- 当函数的指针作为参数时,以下两种写法都可以
func(double f(double))
func(double (*f)(double))
第一个double代表函数返回值,第二个括号里的double代表函数的参数为double类型。在调用函数func的时候,参数里面函数只写函数名,不加括号,如func(sin)
不是func(sin())
- 新建结构体的时候可以采用结构体本身以及结构体的指针两种新建方式
指针方式新建需要在指针初始化时赋予指针一定内存块,而结构体自身的新建方式不需要。注意->
运算等于先对结构体指针取值然后对该值取字段。如下:
#include <stdio.h>
#include <stdlib.h>
struct student{
char* id;
int mark;
};
int main(){
struct student* p=malloc(sizeof(struct student));
p->id="0001"; p->mark=99;
struct student q;
q.id="0002"; q.mark=98;
printf("%s %d\n",p->id,p->mark);
printf("%s %d\n",q.id,q.mark);
return 0;
}
/*
0001 99
0002 98
*/
- 使用枚举类型声明布尔型变量,最好将该定义写在头文件之下,紧邻头文件,使其最早被构建。
typedef enum{FALSE=0,TRUE=1} Bool;
- 初始化数组的时候,若要初始化数组为非0元素,则需要用for循环,
int arr[8]={1};
这种方法是错误的。memset函数不能初始化整数型数组。 - 在函数返回字符数组指针时,要注意,字符数组的作用域一定要包括后面调用其指针的范围,例如:
#include<stdio.h>
/*char newStr[]="hahaha";*/
char* test(){
char newStr[]="hahaha";
return newStr;
}
int main(void){
printf("%s\n",test());
return 0;
}
/*因为newStr数组的作用域仅在test函数里面,所以函数调用完之后,这个数组也就没了,所以主函数无法输出*/
/*如果把newStr作为全局变量,则主函数可以调用到*/
- 在C11中,double类型输出使用
%f
- 在对字符数组进行赋值的时候使用
strcpy
函数,如下:
/*用户输入三行字符串,从小到大输出字符串*/
#include<stdio.h>
#include<string.h>
int main(void){
char arr[3][1000],temp[1000];
for(int i=0;i<3;i++) scanf("%s",&arr[i]);
int flag=1;
while(flag){
flag=0;
for(int i=0;i<2;i++)
if(strcmp(arr[i],arr[i+1])>0){
strcpy(temp, arr[i]);
strcpy(arr[i], arr[i+1]);
strcpy(arr[i+1], temp);
flag=1;
}
}
for(int i=0;i<3;i++) printf("%s\n",arr[i]);
return 0;
}