1. 程序:=数据(变量/常量)+运算(符)
-
数据包括变量和常量,变量必须说明类型。类型规定了变量占据内存的大小
-
运算符有:算术运算符、比较运算符、逻辑运算符、赋值运算符等。
算术运算符: +、-、*、/、%(求余数)、++(自增1)、--(自减1)
比较运算符:>、<、>=、<=、!=(不等于)
逻辑运算符:&&(逻辑与)、||(逻辑或)、!(逻辑非)
位运算符::&(与)、|(或)、^(或)、~
赋值运算符:=、+=、-=、*=、/=、%=、&=、^=、^=
运算符具有不同优先级,记不住优先级,可以用()保证正确运算次序。
http://www.runoob.com/cprogramming/c-operators.html
/* 这里是程序注释,用于对程序进行说明,
比如下面的是求圆的面积程序。
*/
/*输入输出头文件,包含很多输入输出函数如输出函数printf的函数说明(函数规范) */
#include <stdio.h>
/* #define 用于给常量3.1415926起个名字PI,称为宏定义,
PI就是一个宏常量*/
#define PI 3.1415926
int main(){
float r = 4.5; /* 定义了一个叫做r的类型是float(单精度实数类型)*/
float area = Pi*r*r/2; /* 计算圆面积,初始化变量area */
printf("半径是%f的圆的面接是%f\n",r,area);
}
注:
1) /* 和 */内容是程序注释,不是程序指令(语句),仅仅用于对程序进行说明。
2) main是自定义的函数,程序总是从着唯一的main这个主函数开始执行,用左右{}括起来的程序语句称为程序块。
3) printf是一个C语言提供的实现好函数,它的说明在头文件stdio.h里,所以要包含这个头文件。
2. printf和scanf函数
printf是格式化输出函数,其格式为:
int printf("引号括起来的格式串",输出项1,...);
格式串中包含3种字符:
1)普通字符,printf函数会直接输出
2)\开头的字符比如: ‘\n’表示“换行符”、’\t’表示“制表符”,称为“转义字符”printf函数不会输出它们,如果是’\n’,输出会换一行,如果是’\t’,会输出几个空格,还有其他各种转义字符。
3)%开头的叫做“格式符”,表示”格式串”后的输出项按照什么数据类型进行输出,有几个输出项,格式串中就有几个格式符。常见的格式符有:
%c ,表示对应输出项是字符char
%d ,表示对应输出项是整数类型int
%f ,表示对应输出项是单精度实数类型float
%lf ,表示对应输出项是双精度实数类型double
%s ,表示对应输出项是一个字符串,即一串字符
scanf是格式化输入函数,其格式为:
int scanf("引号括起来的格式串",&输入项1,...);
注意:scanf的输入项前必须有一个取地址运算符&,表示传给scanf的这个输入项的地址。
#include <stdio.h>
#include <math.h> /*包含了求开方函数sqrt的定义*/
/*从键盘输入方程系数,输出方程的2个根*/
int main(){
double a,b,c,delta,x1,x2;
scanf("%lf %lf %lf",&a,&b,&c);
delta = b*b-4*a*c;
x1 = (-b+ sqrt(delta) )/(2*a); /*注意用圆括号()保证正确的运算程序*/
x2 = (-b- sqrt(delta) )/(2*a);
printf("方程%lf x^2 + %lf x + %lf的根为%lf,%lf\n",a,b,c,x1,x2);
}
3. 数组
相同类型元素的顺序集合,所有相同元素放在一块连续存储空间中,定义格式为:
格式:
类型 数组名[大小]
类型 数组名[大小] = 初始化式
类型 数组名[] = 初始化式
例如:
int arr[10]; /*大小为10的数组,其中的数据元素没有初始值*/
int arr[10] = {0,1,2,3,4,5,6,7}; /*大小为10的数组,其中前8个数据元素有初始值*/
int arr[] = {0,1,2,3,4,5,6,7}; /*大小为8的数组,每个元素有初始值*/
可以通过 “下标访问运算符[]” 去存取(读写)单独的数据元素。
int arr[10];
arr[0] = 1; /*数组下标从0开始,第一个数组元素为arr[0]*/
arr[9] = 10; /*第10个元素a[9]*/
printf("%d,%d\n",arr[0],arr[9]);
多维数组,可以通过多个下标去存取其中的数据元素,其定义格式类似于一维数组。比如二维数组:
/*定义了一个二维数组,
第一维(称为行)大小是3,
第二维(称为列)大小为4*/
int a[3][4] = {
{0, 1, 2, 3} , /* 行下标为0的第1行的初始化 */
{4, 5, 6, 7} , /* 行下标为1的第2行的初始化 */
{8, 9, 10, 11} /* 行下标为2的第3行的初始化 */
};
/*定义了一个二维数组,
第二维(称为列)大小为4,
第一维(称为行)大小是2,
行数由初始化式子中的个数确定
*/
int matrix[][4] = {
{0, 1, 2, 3} , /* 行下标为0的第1行的初始化 */
{4, 5, 6, 7} , /* 行下标为1的第2行的初始化 */
};
/*修改第3行第4列(下标为[2][3])的元素的值*/
a[2][3] = 23;
字符串
结尾带有“结束字符”即’\0’的字符数组。
#include<stdio.h>
#include <string.h> /*字符串处理函数的头文件*/
int main(){
char str[] = {'h','e','l',''l','o','\0'};
str[0] = 'L'; /*通过下标修改你某个字符*/
printf("%s\n",str); /* 格式符%s表示str是一个字符串 */
/*用字符串函数strlen获得str的字符个数。结束字符不算在内的*/
printf("str字符串的字符个数是%d\n",strlen(str);
str[5] = ','; /*字符数组str不再是一个C语言的字符串了,因为结束字符不是'\0'了 */
printf("%s\n",str); /*错!结果是不可预知的*/
}
4. 循环语句
for系列和while系列两种
for (init初始表达式; test测试表达式; post_proc后处理表达式)
{
循环体程序块
}
含义: 先执行init初始化表达式,然后重复下列循环:
如果"test测试表达式"真 -> 执行body程序块 -> post_proc后处理表达式
直到”test测试表达式”不真。
#include <stdio.h>
int main{
int arr[6],i;
for(i=0; i< 6 ;i++) /*每次i++就使i增加1*/
arr[i] = 1 +2*i;
for(i=0; i< 6 ;i++)
printf("%d\n",arr[i]);
}
while (condition表达式)
{
循环体程序块
}
含义:只要while里得到condition表达式是真的,就一直重复执行”循环体程序块”。
#include <stdio.h>
int main{
int arr[6],i=0;
while(i< 6){
arr[i] = 1 +2*i;
i++;
}
i = 0; /*重新赋值为0*/
while(i< 6){
printf("%d\n",arr[i]);
i++;
}
}
可以用关键字break跳出循环体
#include <stdio.h>
int main{
int arr[6],i=0;
while(1){ /*该条件始终不为0*/
arr[i] = 1 +2*i;
i++;
if(i==6) /*关键字if(如果)判断i<6是否为真*/
break; /*跳出while循环*/
}
i = 0; /*重新赋值为0*/
while(1){ /*该条件始终不为0*/
printf("%d\n",arr[i]);
i++;
if(i==6)
break; /*跳出while循环*/
}
}
5. 条件语句
包括if系列和switch语句
if语句格式:
if (表达式)
{
...程序块
}
含义: 如果“表达式_1为真”,则执行其中的程序块;
```
if (表达式_1)
{
...程序块1
}
else
{
...程序块2
} ```
含义: 如果“表达式_1为真”,则执行程序块1;否则执行程序块2
if (表达式_1)
{
...程序块1
}
else if (表达式_2)
{
...程序块2
}
else
{
...其他程序块
}
含义: 如果“表达式_1为真”,则执行表达式_1对应的“程序块1”; 否则如果“表达式_2为真”,就执行表达式_2对应的“程序块2”; 否则就执行其他情况下的“其他程序块”。
#include <stdio.h>
/* 根据输入整数是否正负等情况,输出不同信息!*/
void main(){
int n;
scanf("%d", &n);
if ( n<0 )
printf (" %d is a 负整数!\n" ,n) ;
else if ( n==0 )
printf (" %d is = 0!\n" ,n) ;
else printf (" %d is a 正整数!\n" ,n) ;
}
switch语句格式:
switch (表达式)
{
case 常量_表达式_1:
...程序块1..
break;
case 常量_表达式_2:
...程序块2...
break;
...
default:
...默认程序块..
}
含义: 就是当里面的switch表达式等于其中的常量时,就执行其中的程序块, 然后用break跳出整个switch语句,如果不等于任何一个常量,就执行默认(default)里的程序块。
#include<stdio.h>
void main( ){
char command;
command = getchar( );
switch( command){
case ' W ' :
printf( " welcome to come here ! \n " );
break;
case 'P':
printf( " I print some information for you \n " );
break;
default:
printf(" You pressed other key \n ");
break;
}
}
6. 函数
对数组中所有数求和
#include <stdio.h>
int main{
int arr[6] = {1,2,3,4,5,6},
arr2[8],i,s;
for(i=0; i< 6 ;i++)
arr[i] = 1 +2*i;
for(i=0; i< 8 ;i++)
arr[i] = (i+1)*(i+1);
i = 0;
for(i=0; i< 6 ;i++)
printf("%d\n",arr[i]);
printf("\n"); /*换一行*/
i = 0;
for(i=0; i< 8 ;i++)
printf("%d\n",arr2[i]);
printf("\n"); /*换一行*/
/*对arr中所有数求和*/
s = 0;
for(i=0; i< 6 ;i++)
s += arr[i] ; /* 等价于 s = s+arr[i]; */
printf("the sum of arr is %d\n",s); /*换一行*/
/*对arr中所有数求和,复制粘帖前面的代码,稍作修改*/
s = 0;
for(i=0; i< 8 ;i++)
s += arr2[i] ; /* 等价于 s = s+arr[i]; */
printf("the sum of arr2 is %d\n",s); /*换一行*/
}
假如有很多个数组要经常求和和输出,可以复制相应代码,做一些调整.
但我们还有更好的方法:可以给这些“求和或输出代码块”起一个名字,然后多次调用这里面的语句,就成了所谓的“函数”。
自己定义个叫做Print的函数,不能与已有函数如printf同名 Print函数用圆括号说明其作用的参数是数组a和数组大小n 参数名a有[],表示a不是一个整数,而是一个数组 Print前的void是这个函数的返回值的类型,因为这里 不需要返回,所以类型为所谓的“无类型”类型void。
#include <stdio.h>
void Print(int a[],int n){
i = 0;
for(i=0; i< 6 ;i++)
printf("%d\n",arr[i]);
printf("\n"); /*换一行*/
}
/*对arr中所有数求和,
sum的返回类型为int,表示返回的是一个整数。
*/
int sum(int a[],int n){
s = 0;
for(i=0; i< 6 ;i++)
s += a[i] ; /* 等价于 s = s+a[i]; */
return s; /*用return关键字返回结果s,其类型为int*/
}
int main{
int arr[6] = {1,2,3,4,5,6},
arr2[8],i,s;
for(i=0; i< 6 ;i++)
arr[i] = 1 +2*i;
for(i=0; i< 8 ;i++)
arr[i] = (i+1)*(i+1);
/*调用自定义的Print函数,将实际参数arr和大小6
分别传给被调用函数Print的形式参数a和n*/
Print(arr2,6);
Print(arr2,8);
/*调用函数sum(arr,6),对arr中所有数求和,
返回结果给main函数的变量s。 */
s = sum(arr,6); /
printf("the sum of arr is %d\n",s); /*换一行*/
/* sum(arr2,8) 返回值直接作为printf函数的输出项*/
printf("the sum of arr2 is %d\n",sum(arr2,8));
}
这种函数“一次定义、多次调用”的行为叫做“代码复用”,可以大大提高编程效率、简化代码。C程序中经常调用各种C函数库提高程序开发效率,如标准库的printf等函数。
函数堆栈
堆栈是一个“后进先出,先进后出”的数据结构,比如一叠碗只能从最上面放入碗,也只能从最上面拿走一直碗。其插入和删除元素只能在一端进行。又比如一头堵死的小巷子只能容纳一个汽车进出,则进出汽车必须按照“后进先出,先进后出”才能正常进出。
每个程序有一个函数运行堆栈,栈顶保存的是保持当前正运行函数的局部变量(包括形式参数)。一个函数开始执行,其局部变量入栈,当它执行结束后,局部变量就出栈。
…
递归函数
调用自身的函数。如
int fc(int n){
if(n==1)
return 1; /*函数结束,直接返回*/
return n*fc(n-1);
}
int main(){
int a = 6;
printf("%d\n", fc(a));
a = 9;
printf("%d\n", fc(a));
}
7. 指针
#include <stdio.h>
int main(){
int a = 3,b=4;
int *p = &a; /*取地址运算符& 用于获取a的地址,
放入整型指针(int *)变量p */
printf("%d\t%d\t%d\t%d\n",b,a,&a,p);
b = *p; /* 取内容运算符*用于获得一个指针指向的变量(内存),
因此*p 就是a */
printf("%d\t%d\n",a,b);
p = a; /*错!a的类型是int(整型),而p的类型是int *(“整型指针”类型)*/
}
指针变量存储的是地址(指针),如p存储的是a的地址。当然可以存储数组的第一个元素的地址(也称为数组的地址,数组名就是数组的地址)。
#include <stdio.h>
int main(){
int a = 3,b,arr[] = {2,3,4,5,6},i;
int *p = &a; /*取地址运算符& 用于获取a的地址,
放入整型指针(int *)变量p */
printf("%d\t%d\t%d\n",b,a,&a,p);
b = *p; /* 解引用运算符*用于获得一个指针指向的变量(内存),
因此*p 就是a */
printf("%d\t%d\n",a,b);
p = arr; /* 等价于 p = &arr[0] */
printf("%d\t%d\n",arr[0],*p);
/* arr[i]等价于 *(arr+i)
p[i]等价于 *(p+i)
*/
for(i = 0 ; i<5;i++)
printf("%d\t%d\t%d\t%d\n",arr[i],p[i],*(arr+i),*(p+i));
}
注意:这里我们学习了2个运算符:取地址&,解引用*
请体会理解其含义!
8. 结构
假如某种类型的对象有多个属性,我们可以将这些属性组合起来,定义一个新的数据类型用于刻画这种类型所有对象的这些共同属性,比如图书有:书名、出版社、作者、出版日期等属性,学生有:姓名、性别、成绩属性,可以用关键字struct来定义相应的结构类型。
结构类型定义格式:
struct 结构类型名{
成员属性
};
如
/*定义一个叫做struct Book的结构类型*/
struct Book{
char name[100];
char publisher[50];
char author[10];
char date[10];
};
/*定义一个叫做struct student的结构类型*/
struct student{
char name[10];
int sex; /*1表示男,0表示女*/
double score; /*学分*/
};
#include <stdio.h>
/*可以定义结构类型的变量*/
int main(){
/*定义struct student类型的变量stu,
stu就是表示一个具体学生信息的变量了.*/
struct student stu;
/*调用字符串拷贝函数strcpy将常量字符串拷贝到stu.name,
访问stu的成员name用成员访问运算符'.'
*/
strcpy(stu.name,"LiPin");
stu.sex = 1;
stu.score = 78.5;
printf("%s\t%d\t%lf\n",stu.name,stu.sex.stu.score);
}
注: strcpy是C字符串的一个拷贝操作,用于将第二个字符串拷贝给第一个字符串.
#include<stdio.h>
#include<string.h>
#define MAX 20
int main(void)
{
char a[MAX]="abc";
char b[MAX]="abcdefghi";
strcpy(a,b);
puts(a); /* puts函数用于输出字符串a*/
puts(b);
}
结构指针
#include <stdio.h>
int main(){
/*定义struct student的变量stu */
struct student stu;
/*定义struct student*的变量p*/
struct student * p;
p = &stu;
/* *p就是stu */
strcpy((*p).name,"LiPin");
(*p).sex = 1;
(*p).score = 78.5;
printf("%s\t%d\t%lf\n",(*p).name,(*p).sex,(*p).score);
/* 还可以用间接访问运算符->去访问p指向的就结构类型变量stu*/
strcpy(p->name,"LiPin");
p->score = 90.5;
printf("%s\t%d\t%lf\n",p->name,p->sex,p->score);
}
9. typedef
给已有类型起一个新名字(别名)。新类型实质就是原有类型。如
typedef unsigned int size_t; /*无符号整型类型unsigned int
的别名为size_t*/
size_t j = 4; /*size_t类型的变量j*/
我们可以用typedef给一个无名结构类型起一个名字,这也,以后定义变量时机不需要每次都要在结构类型名前写”struct”关键字了。如:
typedef
struct { /*无名结构*/
char name[10];
double score;
}student;
int main(){
student stu; /* 类型student前不需要关键字struct了 */
student *p = &stu;
strcpy(stu.name,"LiPing");
stu->score = 80.5;
printf("%s,\t%lf",(*p).name,p->score);
}
10. 动态内存分配
静态数组一旦定义,程序运行时就不能再修改其大小
int arr[100];
struct student students[80];
缺点:
- 万一程序需要存储的整数超过100或学生数目超过80怎么办?
- 另外,一开始分配足够大的空间,会造成空间浪费!假如大多数情况下学生数目不超过30人,程序固定分配80个学生的数组,就是浪费!
改进方法是采用动态内存分配。程序运行过程中,通过调用动态内存分配函数如(malloc,calloc,realloc)向操作系统申请一块大小合适的内存。这块内存用完后应归还给操作系统,即用内存释放(free)。 这些函数可以在 stdlib.h 头文件中找到。
void *calloc(int num, int size);
在内存中动态地分配 num 个长度为 size字节
的连续空间,并将每一个字节都初始化为 0。
返回分配到的内存块的地址
所以它的结果是分配了 num*size个字节长度
的内存空间,并且每个字节的值都是0。
void *malloc(int num);
在堆区分配一块num个字节的内存块,用来存放数据。
这块内存空间在函数执行完成后不会被初始化,它们的值是未知的。
void *realloc(void *address, int newsize);
该函数分配一块newsize个字节的内存块,
并将address指向的旧内存块内容拷贝到新内存块中,
返回新内存块的地址
void free(void *address);
该函数释放 address 所指向的内存块(内存空间)。
#include <stdio.h>
#include<stdlib.h>
int main(){
int n,i;
scanf("%d",&n); /*由用户输入分配的空间大小*/
/*分配课存放n个整数的连续内存块,
sizeof函数用于获得int类型变量
占用内存大小(一个int变量占用多少字节)*/
int *p = (int *)malloc(n*sizeof(int));
/*给空间的每个整数赋值*/
for(i = 0 ; i<n;i++)
p[i] = 2*i+1;
for(i = 0 ; i<n;i++)
printf("%d\n",p[i]);
free(p); /*用完要释放占用的内存*/
}
比如,我们可以用动态内存分配实现C语言已经实现好的字符串拼接strcat函数。
strcat() 函数用来连接字符串,其原型为:
char *strcat(char *dest, const char *src);
先看一下该函数的使用
#include <stdio.h>
#include <string.h> /*字符串处理函数的头文件*/
int main (){
char str[80]; /*字符数组用于存放字符串*/
strcpy (str,"these ");
strcat (str,"strings ");
strcat (str,"are ");
strcat (str,"concatenated.");
puts (str);
return 0;
}
我们自己来实现一个模拟标准库strcat同样功能的Strcat
char* Strat(char *dest,const char *src){
char *p,*q;
int len1 = strlen(dest),len2 = stelen(src);
/*多分配一个字符,用于存放最后的结束字符'\0' */
q = (char *)malloc(len1+len2+1);
if(q==0){ /*内存范培失败,返回值为0即空指针*/
return 0; /*我们也返回0*/
}
free(dest); /*释放原来的dest的空间*/
dest = q; /* dest存储新空间地址,称为指向新空间*/
p = dest; /*第一个字符开头*/
whle( (*p)!= '\0' ){ /* *p不是结束字符*/
*q = *p ;
p++; q++;
}
p = ssrc; /*第2个字符开头*/
whle( (*p)!= '\0' ){ /* *p不是结束字符*/
*q = *p ;
p++; q++;
}
return dest;
}
/* 测试我们自己写的StrCat函数*/
#include <stdio.h>
#include <string.h> /*字符串处理函数的头文件*/
int main (){
char str[80]; /*字符数组用于存放字符串*/
strcpy(str,"these ");
Strcat(str,"strings ");
Strcat(str,"are ");
Strcat(str,"concatenated.");
puts (str);
return 0;
}
您的打赏是对我最大的鼓励!