1. 程序= 数据(变量、常量)+运算(符)
运算符对数据进行运行构成了“表达式语句”,程序由一系列“表达式语句”构成。 表达式语句可分为:普通的表达式语句、控制语句和函数调用语句。 如求一元二次方程的根的程序:
#include <stdio.h> /*包含了输入输出的函数如printf*/
#include <math.h> /*包含了求平方根的函数sqrt*/
int main(){
float a,b,c,delta;
scanf("%f %f %f",&a,&b,&c);
delta = sqrt(b*b-4*a*c);
if(delta<0)
printf("方程没有根\n");
else if(delta==0)
printf("方程的根是%f\n",(-b+delta)/(2*a));
else
printf("方程有2个根是%f\t%f\n",(-b+delta)/(2*a)
,(-b-delta)/(2*a));
}
2. 函数是一个命令的程序块
如果一个语句块(多个语句)可以被多次使用,如上述求方程的根、printf函数的语句块,则可给这个语句块起一个名字,叫做函数。课避免多次重复写相同的代码。
比如,要判断一个整数是否是质数的代码如下:
/*假设n不是一个质数,则n课表示成2个整数a、b的乘积:
n = a*b,其中1<a<b<n 。其中那个小整数a必定是
介于2和n/2之间的整数。
因此,我们只要检查2和n/2之间的每个整数
看看是否能整除n,就能判断n是否是质数了*/
int main(){
int n,a; scanf("%d",%n);
int is_prime = 0;
for(a = 2; a<n/2; a++)
if(n%a==0){
is_prime =1;
break;
}
if(is_prime==1)
printf("%d是一个质数\n",n);
else
printf("%d不是一个质数\n",n);
}
假如程序中需要经常判断一个整数是否是质数,则可以复制粘贴上述代码。
int main(){
int n,a; scanf("%d",%n);
int is_prime = 0;
for(a = 2; a<n/2; a++)
if(n%a==0){
is_prime =1;
break;
}
if(is_prime==1)
printf("%d是一个质数\n",n);
else
printf("%d不是一个质数\n",n);
scanf("%d",%n);
is_prime = 0;
for(a = 2; a<n/2; a++)
if(n%a==0){
is_prime =1;
break;
}
if(is_prime==1)
printf("%d是一个质数\n",n);
else
printf("%d不是一个质数\n",n);
scanf("%d",%n);
is_prime = 0;
for(a = 2; a<n/2; a++)
if(n%a==0){
is_prime =1;
break;
}
if(is_prime==1)
printf("%d是一个质数\n",n);
else
printf("%d不是一个质数\n",n);
}
如此代码会变得很臃肿,我们可以给判断质数的代码块起一个名字, 即变成一个函数,这个函数输入的是一个需要判断是否是质数的整数参数n。
void isPrime(int n){
int a,is_prime = 0;
for(a = 2; a<n/2; a++)
if(n%a==0){
is_prime =1;
break;
}
if(is_prime==1)
printf("%d是一个质数\n",n);
else
printf("%d不是一个质数\n",n);
}
int main(){
int n;
scanf("%d",%n);
isPrime(n);
scanf("%d",%n);
isPrime(n);
scanf("%d",%n);
isPrime(n);
}
这样一来,代码就简洁多了,也节省了敲代码的工作量。函数极大地提高了编程效率!
3. 数组、指针
数组定义:声明其数据元素的类型和数组的大小。如:
int arr[10] = {0, 1, 2, 3} /* 10个int的数组,其余为 0 */
/*二维数组*/
int M[4][5] = {
{ 10, 5, -3}, /*第0行*/
{9, 0, 0 }, /*第1行*/
{32, 30, 1}, /*第2行*/
{0, 0, 8} /*第3行*/
};
/*前5个是第0行,依次类推*/
int N[4][5] = {
10, 5, -3,0,2, 9, 0, 0 , 32, 30, 1, 0, 0, 8
};
/*可以用下标访问数组的元素*/
printf("%d\t%d",arr[4],M[2][3]); /* 转义字符序列\t表示制表符,printf遇到它输出空几个空格*/
假如有一个int变量a,我们可以通过“&取地址运算符”得到a的地址&a,可以将这个地址放入一个int *
类型的变量p中:
int * p = &a;
printf("%d\t %d",*p,a); // *p得到p指向的对象,因此*p就是a
我们可以将数组的地址(第一个元素的地址)放在一个指针变量中。如可以用 &arr[0]得到数组arr的地址, 实际上数组名就是数组的地址,因此:
int *p;
p = &arr[0];
p = arr;
可以通过++或+=n运算符将指向偏移。
/* 对数组元素求和 */
int arraySum (int array[] , const int n) {
int sum = 0, *ptr;
int * const arrayEnd = array + n;
for (ptr = array; ptr < arrayEnd; ++ptr)
sum += *ptr;
return sum;
}
等价于
/* p[i]等价于 *(p+i) */
/* 对数组元素求和 */
int arraySum (int array[] , const int n) {
int sum = 0;
for (int i ; i<n; i++)
sum += ptr[i]; /* p[i]等价于 *(p+i)*/
return sum;
}
4. 字符串
C语言的字符串就是带有结束字符’\0’的字符数组,但字符数组不一定是字符串。如:
char str[20] = {'H','e','l','l','o'}; /*字符数组但不是字符串*/
char str2[20] = {'H','e','l','l','o','\0'} /*字符串*/
/*strlen是查询字符串长度的C语言函数,需要包含头文件<string.h>
*/
printf("%d\t%d",strlen(str),strlen(str2)); /*strlen(str)的结果不可预期*/
C语言提供了很多处理C字符串的函数,这些函数都在头文件
/*src拷贝到dst,返回dst的指针*/
char *strcpy(char *dst, const char *src);
/*src拼接到dst后面,返回新的字符串指针*/
char *strcat(char *dst, const char *src);
实际上,我们完全可以自己实现这些C语言的库函数,如:
/* 计算字符串的长度 */
int stringLength(const char *str) {
const char *cptr = str;
while (*cptr)
++cptr;
return cptr-str;
}
/*to指向的字符数组需要先分配足有的空间*/
void copyString(char *to, char *from) {
for ( ; *from != ’\0’; ++from, ++to )
* to = *from;
*to = ’\0’;
}
练习: 请自己实现一下strcat的功能。
5. 结构、指针
结构就是将一个概念或实体的相关的属性组合在一起,程序员通过关键字struct定义一个结构类型描述一个概念或实体,比如定义一个叫做struct student的结构类型用来描述学生这个概念,正如int描述整数这个概念一样。
我们可以定义结构类型的变量,正如定义一个int型变量一样。
- 通过.(点)运算符访问结构变量的成员
- 通过->(箭头)运算符访问“结构变量指针”指向的“结构变量的成员”
struct student{
char name[10];
double score;
};
int main(){
int a = 3;
int *q = &a; //p存储a的地址,称为指向a
*q = 5;
printf("%d\t%d",a,*q);
struct student stu;
struct student *p;
/*通过.(点)运算符访问结构变量的成员*/
strcpy(stu.name,"LiPing");
stu.score = 80.5;
printf("%s,\t%lf",stu.name,stu.score);
p = &stu;
/*通过->(箭头)运算符访问“结构变量指针p”指向的“结构变量的成员”
strcpy(p->name,"ZhangWei");
p->score = 60.5;
printf("%s,\t%lf",p->name,p->score);
/*--- *p 就是stu --- */
strcpy((*p).name,"ZhangWei");
(*p)score = 60.5;
printf("%s,\t%lf",(*p).name,(*p).score);
}
6 typedef
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(p->name,"LiPing");
stu.score = 80.5;
printf("%s,\t%lf",(*p).name,p->score);
}
7. C++的单行注释//
除了C语言的块注释 /*
、*/
外,
C++还提供了对一行进行注射的//
//这是一个hello world程序
int main(){
cout<<"hello world";
}
8. C++的输入输出对象:cin、cout
我们可以用C++的输入输出对象:cin、cout来代替C语言的scanf、printf,再也不用为C语言输入输出格式化规则烦恼了。因为cin、cout能自动识别输入输出的变量类型。
这是一个关于scanf、printf的C语言程序
#include <stdio.h>
int main(){
char ch;
int a;
double d;
char str[10];
scanf("%c %d %lf %s",&ch,&a,&d,str);
printf("%c %d %lf %s",ch,a,d,str);
}
而C++通过对输入流对象cin、输出流cout分别调用输入运算符»、输出运算符«。数据将像流水一样自动流进流出变量。
cin、cout分别代表键盘和屏幕对象。
#include <iostream> //C++的标准输入输出流头文件
#include <string> //C++的字符串类型string的头文件
/*为避免不同库(程序之间)出现名字冲突(同名),
C++引入了名字空间namespace,即名字是属于某个名字空间的
如同不同单位出现同一个人名一样,可以通过单位名区分开来
*/
using namespace std; //string,cin,cout等都定义在名字空间std中
int main(){
char ch;
int a;
double d;
string str; // C++的字符串类型不需要规定大小
cin>>ch>>a>>d>>str; //数据从cin流入到变量中
cout<<ch<<a<<d<<str; //数据从变量流出到cout中
}
9. C++的动态内存分配
C语言通过malloc等分配内存,都需要将返回的void指针类型(void *
)转化为变量的特定类型指针。而C++用于内存分配的运算符new返回的内存块地址类型会自动识别,C++用于内存分配的new运算符和释放内存块的delete还可以自动调用类的构造和析构函数对对象进行初始化和销毁。更加方便安全。
#include <iostram>
int main(){
int *p = new int; /*分配1个整数的空间,
类似于C的: int * p = (int *)malloc(sizeof(int))*/
int *q = new int[8]; /*分配8个整数的空间
类似于C的: int * q = (int *)malloc(8*sizeof(int))*/
*p =3;
*q = 4;
*(q+1) = 5;
*(q+2) = 7;
*(q+5) = 8;
q[3] = 6; //q[3]等价于 *(q+3)
*(p+1) = 1; //错:p+1指向的内存块不属于你的程序!
cout<<*p<<"\n\n";
for(int i = 0 ; i<8;i++)
cout<<q[i]<<"\n";
//用delete释放用new分配的内存块
delete p; // 释放单个元素内存块
delete[] q; //释放多个元素内存块
return 0;
}
练习:请实现一个用单链表存储学生信息的程序,具有:输入、输出、插入、删除、查找等功能。提示,课一定下述结构类型:
typedef struct{
string name;
double score;
}student;
typedef struct lnode{
student data;
struct lnode *next;
}LNode;
10. C++的引用
注: 这部分内容是C++的,为了下面的数据结构课程做准备以便简化函数的参数传递
在变量名前加一个修饰符& (注意这不是取地址运算符),说明这个变量是引用变量,也就是说这个变量将引用其他变量,是其他变量的别名! 有点类似typedef给已有类型其一个别名一样,这是给已有变量起一个别名,也就是这个引用变量和被引用的变量是同一个变量。
int main(){
int a = 3;
int &b = a; /*定义引用变量b,它引用的是a,
因此,b就是a
也就是b和a指示的是同一块内存。*/
b = 10;
printf("%d\t%d\n",a,b);
}
假如写一个交换变量的代码
int main(){
int a = 3,b=4;
int t = a; a = b; b = t;
printf("%d\t%d\n",a,b);
}
我们看到a,b的值发生了交换。
现在,我们将上述交换代码变成一个独立的函数
void swap(int x, int y){
int t = x; x = y; y = t;
}
int main(){
int a = 3,b=4;
swap(a,b);
printf("%d\t%d\n",a,b);
}
为什么没有发生交换? 这是因为C语言的参数传递是“传值”(实际参数的值拷贝给形式参数,实际参数和形式参数占据各自独立的内存块)。尽管在swap函数中x,y确实发生了交换,但和main函数的参数a,b毫不相干,回到main函数后,swap函数的参数已经销毁。
如何使得a,b交换呢? 可以将a,b的地址(指针)作为参数传给swap。
/*通过x,y对它们指向的数据进行修改*/
void swap(int *x, int *y){
int t = *x; *x = *y; *y = t;
}
int main(){
int a = 3,b=4;
swap(&a,&b); /*a,b的地址传递给swap*/
printf("%d\t%d\n",a,b);
}
在C++语言中,我们可以将swap的2个形式参数定义为“引用参数“,使得形式参数就是实际参数的别名,也就是引用参数和它引用的实际参数是同一块内存,这种参数传递方式叫做“传引用”。
/* 通过x,y对它们引用的内存进行修改,
x就是a,y就是b,对x,y的修改就是对a,b的修改
*/
void swap(int &x, int &y){
int t = x; x = y; y = t;
}
int main(){
int a = 3,b=4;
swap(a,b);
printf("%d\t%d\n",a,b);
}
引用变量的注意事项
- 因为b是引用变量,所以在定义它时就必须指明它引用的是那个变量,而且一旦引用某个变量,就不能再引用其他变量
您的打赏是对我最大的鼓励!