程序设计和C语言
C语言的特点
C语言语句简洁紧凑 使用方便灵活
运算符丰富 表达能力强
//c语言只有32个关键字 34种运算符
C语言程序可移植性好
//C语言中没有直接依赖硬件的语句 与硬件有关的操作 如数据的输入、输出都是都是调用系统提供的库函数
//来实现的 而这些库函数本身并不是C语言的组成部分 编写的程序很容易由一个系统移植到另一个系统
生成的目标代码代码质量高 运行效率高
//可编写嵌入式系统程序
语言处理能力强
C语言是一种结构化语言
结构化程序设计原则:
自顶向下(先总体,后细节);
模块化(分解总目标);
逐步求精(设计子目标过渡);
限制使用goto语句。会影响程序结构化风格
结构化程序的基本结构与特点:
只有一个入口;只有一个出口;
每一个基本结构中的每一部分都有机会执行到
结构内不存在“死循环”
C语言是结构化程序 任何程序都可以由顺序、选择(分支)、循环(重复)构成
清晰第一 效率第二
C程序开发过程
编辑—>(.c)编译—>(.obj)链接—>可执行文件(.exe)
标准库函数按分类在不同的头文件中声明
用户不可以重新定义标准库函数
数据类型、运算符、表达式
数据类型
基本类型 整型、字符型、实型
构造类型 数组类型、结构体类型、共用体类型、枚举类型
指针类型
空类型
整形常量
unsigned U/u
long int L/l
10进制 正负号组成
8进制以0开头 一般是无符号数 010
16进制以0x/0X开头 一般是无符号数A-F大小写均可 0x19Ad
#include<stdio.h>
int f(int x, int y) {
return x / y + x % y;
}
int main(void)
{
float a = 1.5, b = 2.5, c = f(a, b);
printf("%.2f", c);//1.00
//float 类型不指定长度默认6位精度
return 0;
}
//题太牛逼了 好好体会!
字符串常量
"helloworld"
//字符串常量在内存中占用一段连续的存储单元 系统自动在末尾加上'\0' 作为字符串结束符
"hello\0world"
//长度是5 sizeof()是12
'6'和"6"占用字节不一样
#include<stdio.h>
int main(){
const char *s="helloworld";
printf("%ld %s %c",sizeof("helloworld\0"),s,*s);
//指针指向字符串常量的首地址 s取字符串 *s取字符串首地址存的字符
return 0;
}
变量的位置
//全局变量(external variable)和静态变量 (static variable)的初始化式必须为常量表达式
//只能采用初始化的方式进行赋值 函数外部不能进行赋值操作!
#include<stdio.h>
int a[10];
int *p=a;
//p++;
int b=2;
int c;
//c=b;
在函数外部只能声明变量 不能
int main(){
//int *p=a;
//p++;
c=b;
a[1]=1;
printf("%d",*p);
}
在函数外部定义的变量称为全局变量。
带static的是静态全局变量, 作用域为当前文件。不带static的是全局变量, 作用域为整个程序。
所有全局变量的生命周期都是整个程序运行
存储类型关键字
auto
//不赋值 默认值不正确 当程序执行到定义他的函数或者语句时才分配 当函数结束后自动释放
//局部变量默认
register
//将变量的值存在CPU寄存器中 而不是占用内存单元
static
//在编译时分配内存空间 所占用的存储单元直到程序结束才释放
//初始值默认为0
extern
外部变量 是在程序中声明已在函数外部的定义过的全局变量
extern只能声明已经存在的变量! 不能定义变量
外部变量的作用域是从变量的定义处到本程序的结束 在此作用域内 外部变量可以为程序中各个函数所调用
如果在定义点之前的函数想引用全局变量 则应该在引用之前用关键字extern对该变量进行声明 表示该变量是一个已经定义的外部变量
#include<stdio.h>
int imax(int x.int y){
return a>b?a:b;
}
int main(){
//此位置
extern a,b;
printf("%d\n",imax(a,b));
return 0;
}
//两个变量定义在程序最后 在主函数中必须用extern进行声明 否则会报错
int a=10,b20;
char 特性
char a[]="abcd";//末尾有\0结束符 比b数组的长度大
char b[]={'a','b','c','d'};//可以不指定长度
const char *s="abc";//定义指针char字符串
char str[3]={'a','a'};//定义字符数组 最后位置自动补零
const char *arr[]={"aaa","bbb"};//定义字符串数组
//接收字符数组 传入数组名即可!!
scanf("%s%s",s1,s2);
字符串是字符数组来存储的 默认结尾\0作为标志
char str[5];
str="china";//错误!
str[5]="china";//错误!
即不能在定义字符数组以后 对数组名进行赋值操作!!!
字符数组在定义时未进行初始化 各元素的初始值是不确定的
但是若初值表中处置的个数小于字符数组的长度 多余元素初值为0
strlen和sizeof
#include<stdio.h>
int main(){
char a[7]="a0\0a0";
printf("%d %d",sizeof(a),strlen(a));
//7 2
//strlen计算字符串的长度不包括\0标识符 读到\0自动结束
return 0;
}
字符串保存到字符数组中
char a[30],*p=a;
a[30]="this is my name";//a[30]赋值给字符串显然是错误的
a="this is my name";
p="this is my name";//类型转换错误
strcpy(p,"this is my name");//正确赋值
运算符
数据类型混合运算时:
char ->short -> int -> long -> float -> double
%左右必须都是整数
#include <stdio.h>
int main() {
extern int ifun(int);
static int i;
int n = 0;
for (int i=0; i < 4; i++) {
n += ifun(i);
}
printf("%d %d\n",i, n);
int test();//声明调用再定义
printf("%d\n", test());
return 0;
}
int ifun(int k) {
static int i;
int j = 0;
return ++i + k + j++;
}
int test() {
int i = 1;
int j = 2;
return ++i+j++;
}
//好好体会结果!
位运算
20|1=21
逻辑运算进行逻辑运算时,若&&左边的值为0,则不再对右边的运算对象进行运算,整个表达式的值为0。
1.若有定义语句:int k1=10,k2=20;,执行表达式(k1=k1>k2)&&(k2=k2>k1)后,k1和k2的值分别为 (B)
A. 0和1 B.0和20 C.10和1 D. 10和20
【解析】k1>k2为假,因此k1= k1>k2结果0,逻辑与左边表达式为假,右边表达式不再处理,因此k1结果为0,k2不变,仍为20
函数
先声明再调用又定义
先定义再调用
直接递归调用 间接递归调用
return 语句
return 表达式、变量、值
内部函数
内部函数:
函数只能被本文件中其他函数所调用
格式:
static 返回值类型 函数名(形参表);
又被称为静态函数
好处:
函数的作用域只在本文件中 在由多人分别编写不同的程序模块时
不同担心自己所用的函数名与别人的是否相同
即使函数名相同 也不会产生干扰
外部函数
外部函数:
函数允许被其他文件中的函数所调用
格式:
extern 返回值类型 函数名(形参表)
有效期:
外部函数在整个源程序都有效
事实上C语言规定 如果再定义函数时省略extern 默认为外部函数
外部函数定义后 需要在本函数中显式声明表示此函数已在其他文件中定义
func((a,b,c),(d,e));
//几个参数 是否合法?
若调用一个函数且此函数中没有return语句 则返回一个不确定的值
函数值类型的定义可以缺省 此时函数值的隐含类型是int
#include<stdio.h>
//extern 可以省略
extern long func(int x);
int main(){
//由于函数func在main主函数下表 会识别不到 所以需要声明一下
//extern long func(int x); 这个位置也可以声明
printf("%ld\n",func(132645));//结果贼牛逼365!
return 0;
}
long func(long x){
if(x<100)return x%10;
else return func(x/100)*10+x%10;
}
数组与函数
数组元素作为函数实参值传递 形参和实参占据系统分配的不同内存单元
数组名作为函数参数时 所进行的传递是地址 所以操作的时同一段内存单元
指针与函数
函数指针变量:
是指向函数的指针变量定义形式
如何定义:
类型说明符 (*指针变量名)(参数列表)
eg. int (*fp)(int);
指针型函数 返回指针的函数体
格式输入函数scanf()
le 科学计数法:e E后必须为整数(1.0不行!!)
(1)一般形式:scanf(“格式控制字符串”,地址列表);
(2)若格式控制字符串中有非格式字符串,则按原样输入。若格式控制字符串中没有非格式字符作输入数据之间的间隔,则可用空格、Tab、回车作间隔。
(3)在输入字符数据时,若格式控制字符串中无非格式字符,则认为所有输入的字符均为有效字符。空格会被当作字符赋值给变量。
(4)当定义的变量类型和scanf中“格式符”类型不一致时(这里只限定在字符型和整型两种类型,其他数据类型不可以。)整型和字符型之间通过ASCLL码值可以相互转化。
1.若有定义:inta,b;通过语句scanf(“%d;%d”,&a,&b);,能把整数3赋给变量a,5付给变量b的输入数据是
A.3 5 B.3,5 C.3;5 D.35
【解析】scanf格式字符串中两个%d之间带有非格式字符分号,运行时应原样输入
2.有以下程序
#include <stdio.h>
main()
{int a1,a2; char c1,c2;
scanf(“%d%c%d%c”,&a1,&c1,&a2,&c2);
printf(“%d,%c,%d,%c”,a1,c1,a2,c2);
}若想通过键盘输入,使得a1的值为12,a2的值为34,c1的值为字符a,c2的值为字符b,程序输出结果是:12,a,34,b则正确的输入格式是(以下_代表空格,
A)12a34b
\1. 【解析】空格会被当作字符赋值给变量,故B、D错误;C项中逗号会被当作字符赋值给变量,故C项错误。3.有以下程序#include <stdio.h>
main()
{int x,y;
scanf(“%2d%ld”,&x,&y); printf(“%d\n”x+y);
}程序运行时输入:1234567程序的运行结果是 34579 。
【解析】%2d输入的数据宽度为2,x的值为12,y的值为34567,x+y=34579。
循环结构
for while do-while
1.有以下程序
#include<stdio.h>
int main(){
int n=5;
do{
switch(n%2){
case 0:n--;break;
case 1:n--;continue;
}
n--;
printf("%d\n",n);
}while(n>0);
//2 0
return 0;
}
选择结构
break语句用于循环体、switch语句(跳出switch语句并执行其他的语句)
continue语句只能用于循环体!
if语句
内嵌结构中,else总是与前面最近的且未曾配对的if语句配对,组成一对if-else语句。
switch-case语句
1.有以下程序
int main()
{ int s;
scanf("%d",&s);
while(s>0)
{ switch(s)
{case1:printf("%d",s+5);
case2:printf("%d",s+4);break;
case3:printf("%d",s+3);
default:printf("%d",s+1);break;
}
scanf("%d",&s);
}
}
运行时,若输入1 2 3 4 5 0<回车>,则输出结果是(A)
A)6566456 B)66656 C)66666 D)6666656
【解析】while循环s<=0时退出循环,输入1 2 3 4 5 0,只有当输入0时退出循环,switch中当s=1时,执行case 1,case 2,由于case 1后面没有break,会继续执行case 2,遇到break,退出switch;当s=2时,执行case 2,退出switch;当s=3时,执行case 3,default,由于case 3后面没有break,会继续执行default,遇到break,退出switch;当s=4和5时,执行default,退出switch。所以输入1时,输出65;输入2时,输出6;输入3时,输出64;输入4时,输出5;输入5时,输出6。故选择A。
2.有以下程序
#include<stdio.h>
int main(){
int a=0,b=2;
printf("++a,ab结果:%d\n",(++a,ab));//逗号表达式结果为2
switch(++a,a*b){
case 1: printf("1");
case 2:printf("2");
case 3:printf("3");
default: printf("other\n");
//default语句如果在第一行 可以完全不执行的
}
//非常有意思的代码 自己体会!
return 0;
}
数组
数组在内存中占一片连续的存储区 由数组名代表它的首地址
C程序在执行过程中不会检测数组下标是否越界
int i=5,a[i];//错误 不允许用变量对数组的大小进行定义
int a[][3]={{1},{2},{3}} //二维数组行可以缺省 本质上和一维数组内存中存放方式是一样的
#include<stdio.h>
int main(){
int i,j,a[3][3];
for(i=0;i<3;i++)
for(j=0;j<3;j++)
{
if(i+j==3)a[i][j]=a[i-1][j]+1;
else a[i][j]=j;
printf("%4d",a[i][j]);
}
printf("\n");
return 0;
}
// 0 1 2
// 0 1 3
// 0 2 2
#include<stdio.h>
int main()
{
int i, j, a[10];
a[0] = 1;
for (i = 0; i < 5; i++)
for (j = i; j < 5; j++)
{
a[j] = a[i] + 1;
printf("j:%d %d\n", j, a[j]);
}
for (i = 0; i < 5; i++)
printf("%4d",a[i]);
return 0;
}
// 2 4 6 8 10
//优雅的代码 自己体会
指针
指针就是地址 指针变量用来存储地址 而一般变量用来存储数组
*取值 &取址 &&短路与
#include<stdio.h>
int main(){
int m=1;
int *p=&m;
printf("%p %p %p\n",&m,&p,p);
//p存储的是m变量的地址
return 0;
}
od@wp:~/c_project/demo5$ ./t
0x7ffee261dc8c 0x7ffee261dc90 0x7ffee261dc8cod
指针加整数是连续移动若干地址
#include<stdio.h>
int a[10],*p;
p=a,p++;
char a[] = { 'a' ,'b','c'};
char* p = a;
for (int i = 0; i < 4; i++)
//++和一个优先级 结合顺序从右到左 即p++等同于*(p++)
printf("%c", p++);//a++是错误的表达!!
int a[] = { 1,2,3,4,5 }, * p;
int i;
for (i = 0, p = a; p < a + 5; p++, i++)printf("%d", p[i]);
//直接越界
printf("%d", p[0]);
//这种方式拿到元素值
for(p=a;p<a+5;p++)printf("%d",p[0]);//还能用指针取下标操作拿到元素值!
int a[5]
//拿到a[1]的地址
//&a[1] 1+a &a[0]+1
//a++ ++a这种肯定拿不到 因为a是数组 默认是数组首地址 不能移动变量
这段代码绝了 好好领悟一下
#include<stdio.h>
int main(){
extern fun(int a[],int *p);
int a[5]={18,2,16,3,6},x=5,y;
y=fun(a,&x);
printf("%d %d",x,y);
return 0;
}
int fun(int a[],int *p){
int i,n;
n=*p;
p=&a[n-1];
for(i=n-2;i>=0;i--)
if(a[i]>*p)p=&a[i];
return *p;
}
//结果 5 18
指针操作的赋值
#include <stdio.h>
extern int func(int *x,int *y,int z);
int main(void){
int a = 1, b = 2,c=3,d;
d = func(&a, &b,c);
printf("%d %d %d", a, b, c);
return 0;
}
int func(int *x,int *y,int z) {
*x = *y;
*y = z;
z = *x;
return z;
}
// 2 3 3
int main() {
char a[5] = { 'a','b' };
char* p = a;
for (int i = 0; i < 2; i++) {
printf("%c", *(++p));//结合顺序是从右到左 即加不加括号都行
printf("%c", *(p++));//不一样的结果
printf("%c",*a++);//错误操作
}
int main() {
int a[] = { 1,2,3,4,5 }, * p, i;
for (p = a, i = 0; i < 4; i += 2)printf("%d", p[i]);
//p存储的就是a数组的首地址 可直接操作
return 0;
}
二维数组与指针
#include<stdio.h>
void main() {
int b[][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
printf("%d", ((b+1)+1));
//6
}
访问数组元素三种方式
a[i][j] *(a[i]+j) ((a+i)+j)
//访问二维数组元素
结构体
如何定义?
方式1
struct 结构体变量名{类型 成员名...;};
struct 结构体名 变量名;
方式2
struct 结构体变量名{类型 成员名;...}变量名(可定义多个 中间用逗号隔开);
方式3
struct {类型 成员名}变量名;
此形式省略了结构体名 预示着后续程序不在定义该类型的变量
方式4
typedef struct 结构体变量名{类型 成员名;...}别名;
别名 变量名表列;
成员引用?
结构体名.成员名
(*指针变量名).成员名
指针变量名->成员名
// () -> . 同级别 均大于*
结构体变量与函数调用
用结构体变量的成员做参数 单向值传递
用结构体变量做实参 对应形参也是同类型结构体变量
用指向结构体的变量(或数组)的指针作实参 属于地址传递
sizeof() for sturct
When applied to a structure type or variable, sizeof returns the actual size, which may include padding bytes inserted for alignment.
struct s{
int i;
cahr c;
}s1;
//sizeof(s1)等于多少?
//4+1 肯定5嘛 but 实际是8
//字节对齐加快计算机的取数速度 加快取指令周期
链表的代码
#include <stdio.h>
struct node {
int k;
struct node* next;
};
int main(void){
struct node m[5], * p = m, * q = m + 4;
int i = 0;
while (p != q) {
p->k = ++i; p++;
q->k = i++; q--;
}
q->k = i;
for (i = 0; i < 5; i++)
printf("%d", m[i].k);
return 0;
}
//13431
struct st {
int x;
int* y;
}*p;
extern int dt[4] = { 10,20,30,40 };
struct st aa[4] = { 50,&dt[0],60,&dt[1],70,&dt[2],80,&dt[3] };
int main() {
p = aa;
printf("%d", ++p->x);
printf("%d", (++p)->x);
printf("%d", ++(*p->y));
return 0;
}
//51 60 21
共用体
union 使得几个不同类型共占同一段内存(相互覆盖)
结构体和共用体区别:
存储方式不同:结构体变量占用的内存单元是其所有成员内占内存单元的总和 而共用体变量是其所有成员中占用空间最大的成员的内存单元
#include<stdio.h>
int main(){
//开辟4bytes空间
union {
char ch[2];
int d;
}s;
s.d=0x4321
printf("%x,%x\n",s.ch[1],c.ch[2]);
//21 43
}
文件
分类
ASCII文件
//又称为文本文件
//以ASCII码值进行存储与编码的文件 其文件内容就是字符
//编码方式公开可以被不同文本编辑器识别 缺点效率低信息冗余度高
二进制文件
//存储二进制数据的文件 包含的内容是可以识别的机器代码 记事本打开会乱码
//将信息在内存中的二进制形式存储到文件中 存储效率高
本质上讲 ASCII二进制文件没有区别 所有文件在外存储器上都只有一种存储方式 比特
在C语言中文件按照顺序构成的比特序列,将比特序列划分成字符流和字节流这样的文件称作流文件
缓冲区
系统在内存中开辟缓冲区将外存文件的数据读到缓冲区 程序将缓冲区内容接收到内存的变量
系统每次读文件时 会将文件中的一批数据读到输入缓冲区 当所需要的数据不在缓冲区
系统将再次从文件中读入一批数据
写文件时 则在输出缓冲区满时才会有实际的写文件操作 输出缓冲区未满或者关闭之前
实际的写操作并不会执行
减少程序对外存的访问 提高文件操作速度
文件打开
#include<stdio.h>
int main(){
char str[51];
FiLE *fp;
if((fp=fopen("d:\file.txt","r"))==NULL){
printf("File open error\n");
exit(1);
}
fgets(str,51,fp);
printf("%s(from file file.txt)",str);
if(fclose(fp)){
printf("can not close the file!\n");
exit(1);
}
return 0;
}
read
"r" | 只读 文本文件 | 文件必须存在 否则fopen()函数返回NULL |
---|---|---|
"r+" | 读写 文本文件 | |
"rb" | 只读 二进制文件 | |
"rb+" | 读写 二进制文件 |
write
"w" | 只写 文本文件 | 文件存在:清空原内容 文件不存在:新建打开 |
---|---|---|
"w+" | 读写 文本文件 | |
"wb" | 只写 二进制文件 | |
"wb+" | 读写 二进制文件 |
append
"a" | 只写追加 文本文件 | 文件存在:原内容追加 文件不存在:新建打开 |
---|---|---|
"a+" | 读写追加 文本文件 | |
"ab" | 只写追加 二进制文件 | |
"ab+" | 读写追加 二进制文件 |
文件关闭
fclose(FILE *fp);
//Success 0
//Error EOF(-1) EOF是stdio的头文件宏常量为-1
编译预处理
当对一个源文件进行编译时 系统首先引用预处理程序对源程序的预处理部分做处理 处理完毕后自动对源程序进行编译
功能
宏定义 文件包含 条件编译
宏定义
不带参数的宏定义(#define 宏名 字符串/数值) !!注意末尾末尾不用分号
带参数的宏定义(#define 宏名 (参数表) 字符串) 不占运行时间 只占编译时间 只是简单的机械替换
#带参数的宏调用和函数调用区别
1.函数调用时如果实参是表达式要先求出表达式的值 再做为形参传递给形参
2.函数调用在运行时候处理 宏调用预编译的时候处理
3.宏不存在类型 宏名无类型 形参也无类型
4.宏定义函数调用会使源程序会变长
5.函数调用则占用运行时间
#define f(x) xxx 宏定义中参数没有类型 仅为字符序列 不能当表达式运算 宏展开时把实参字符原样写在替换文本中
#define P1 int * //宏bi定义可以定义运算符、表达式
typedef int * p2;//typedef是在编译的时候完成的 不是简单的替换
P1 a,b;//int *a,b;只做字符替换
p2 a,b;//int a,b;
文件包含
预编译程序在执行#include命令会将包含文件插入到所在的位置 合并成一个文件进行编译 得到一个目标程序(.obj)
#include<文件名>
//编译系统从启动C编译系统的文件夹中去寻找所要包含的文件 称为标准模式
or
#include"文件名"
//编译系统先从当前文件夹去找所要包含的文件 找不到再按标准模式找
易错点
#define S(x) x*x
int K=4;
++S(K+1); //区分++S(4+1)
//++K+1*k+1 注意K值会发生变化
#define M(a,b) -a/b
M(4+3,2+1);//结果-1 -4+3/2+1
#include<stdio.h>
int x = 1;
int main() {
extern int f(int x);
printf("%d", f(x));//
return 0;
}
#define x 2
//作用域从定义处到源文件结束
int f(int y) {
return x + y;
}
#include <stdio.h>
#define PI 3.14
double c(float r) {
return PI * r * r;
}
int main() {
printf("%f", c(1));//注意结果是3.140000
return 0;
}
#define max(a,b) a>b?2a:2b
int main(){
printf("%d", max(1 + 2, 3 + 4));
//注意是10 不是14
return 0;
}
#include<stdio.h>
#define LAB1 0
#define LAB2 2
#include<stdio.h>
void main() {
int temp;
#ifdef LAB1
temp = LAB1;
#else
temp = LAB2;
#endif // LAB1
printf("temp=%d\n", temp);
}
#define MIN(a,b) (a)<(b)?(a):(b)
int main() {
int k = 10 * MIN(20, 25);
printf("%d",MIN(20,25));
return 0;
}//结果25
//狠题!!
标签:main,return,int,C语言,++,printf,include From: https://www.cnblogs.com/odfive/p/17598855.html