1.在学习“九九口诀表”的时候,对for循环突然有了很严重的认知缺陷。
for语句的一般形式:
for(循环变量赋初值;循环条件;循环变量增值)
例如:for(s=2;s<=10;s++){ }
表达式1 表达式2 表达式3 都可以省略。
表达式1省略:此时应在for语句之前给循环变量赋初值。
表达式2省略:既不判断循环条件,循环无终止的进行下去,也就是表达式2始终为真。
表达式3省略:但此程序设计者应另外保证循环能正常结束。
注:省略表达式时,它后边的“ ;”不能省略。
源代码:
#include int main()
{
int n;
scanf("%d", &n);
int i,j;
for(i = 1; i <= n; i++)
{
for(j = 1; j <= i; j++)
{
printf("%d*%d=%-3d", j, i, i * j);
}
printf("\n");
}
return 0;
}
但是只看源代码看不出for循环中参数的变化,所以在里边的特定位置加上打印来查看,如下:
#include int main()
{
int n;
scanf("%d", &n);
int i,j=0;
for(i = 1; i <= n; i++)
{
printf("j=%d\n",j);
for(j = 1; j <= i; j++)
{
printf("for i=%d,j=%d",i,j);
printf("%d*%d=%-3d", j, i, i * j);
}
printf("\n");
}
return 0;
}
取n=2,输出如下:
sunyuhang@666:~/c$ gcc 1.c -o 1
sunyuhang@666:~/c$ ./1
2
j=0
for i=1,j=11*1=1
j=2
for i=2,j=11*2=2 for i=2,j=22*2=4
2.定义函数然后调用函数
#include#includeint nums(int x) //定义函数
{
int i;
if(x==1)
{
return 0;
}
for(i=2;i*i<=x;i++)
{
if(x%i==0)
{
return 0;
}
}
return 1;
}
int main()
{
int m,n,j;
scanf("%d %d",&m,&n);
int count=0;
int sum=0;
for(j=m;j<=n;j++)
{
if(nums(j)==1) //调用函数
{
count++;
sum+=j;
}
}
printf("%d %d",count,sum);
return 0;
}
3.从函数中返回
3.1
int max(int a, int b)
{
int ret;
if(a>b){
ret=a;
}else{
ret=b;
}
return ret; //只有一个return,符合单一出口
}
此处定义了函数max,它返回一个int类型。
return语句:return停止函数的执行,并返回一个值。
3.2 没有返回值的函数
形式:void 函数名(参数表)
不能使用带值的return;可以没有return;调用的时候不能做返回值的赋值。
如果函数有返回值,则必须使用带值的return。
void sum(int begin, int end)
{
int i;
int sum=0;
for(i=begin; i
4.函数参数
如果函数要使用参数,则必须声明接受参数值的变量。这些变量称为函数的形式参数。
形式参数就像函数内的局部变量,在进入函数时被创建,退出函数时被销毁。
当调用函数时,有两种向函数传递参数的方式:
1.传值:
该方法把参数的实际值复制给函数的形式参数。在这种情况下,修改函数内的形式参数不会影响实际参数。
#includevoid swap(int a, int b);
int main()
{
int a=5;
int b=6;
swap(a,b);
printf("a=%d, b=%d\n", a,b);
return 0;
}
void swap(int a, int b)
{
int t=a;
a = b,
b = t;
}
输出为:
a=5, b=6
可以看出,上述代码并不能交换a和b的值。
当上述代码在做swap(a,b)时,只是把a和b的值传给了swap函数中的a和b。除此之外,swap函数中的a,b和main中的a,b完全没有关系。所以,在swap函数中对参数a和b做的任何事情,和main中的a和b没有任何关系。因此,不能交换a和b的值。
2.传指针:
通过指针传递方式,形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作。
传递指针可以让多个函数访问指针所引用的对象,而不用把对象声明为全局可访问。
#includevoid swap(int *x, int *y); //函数声明
int main()
{
int a=100; //局部变量定义
int b=200;
printf("交换前a的值:%d\n", a);
printf("交换前b的值:%d\n", b);
swap(&a, &b); //调用函数来交换值
printf("交换前a的值:%d\n", a);
printf("交换前b的值:%d\n", b);
return 0;
}
//定义函数
void swap(int *x, int *y)
{
int temp;
temp = *x;
*x = *y;
*y = temp;
return;
}
输出为:
交换前a的值:100
交换前b的值:200
交换前a的值:200
交换前b的值:100
该实例表明,与传值调用不同,传指针调用不但在函数内部改变了a和b的值,实际也改变了函数外a和b的值。
C语言在调用函数时,永远只能传值给函数。这意味着函数内的代码不会改变用于调用函数的实际参数。
所以:
1.每个函数都有自己的变量空间,参数也位于这个独立的空间中,和其他函数没有关系。
2.过去,对于函数参数表中的参数,叫做“形式参数”(如上述代码swap函数的int后边的a和b);调用函数时给的值,叫做“实际参数”(如上述swap(a,b)这条语句中的a和b)。
5.局部(本地)变量/全局变量
5.1 局部(本地)变量
在某个函数或块的内部声明的变量称为局部变量,它们只能被该函数或该代码块内部的语句使用。局部变量在函数外部是不可知的。如下所示,a,b,c是main()函数的局部变量。
#include int main ()
{
/* 局部变量声明 */
int a, b;
int c;
/* 实际初始化 */
a = 10;
b = 20;
c = a + b;
printf ("value of a = %d, b = %d and c = %d\n", a, b, c);
return 0;
}
5.2 静态本地变量
在本地变量定义时加上static修饰符就成为静态本地变量;
当函数离开的时候,静态本地变量会继续存在并保持其值;
静态本地变量的初始化只会在第一次进入这个函数时做,以后进入函数时会保持上次离开时的值;
静态本地变量实际上是特殊的全局变量;
静态本地变量具有全局的生存期,以及作为本地变量所具有的函数内的局部作用域。
#includeint f(void);
int gall=12;
int main(int argc, char const *argv[])
{
f();
f();
f();
return 0;
}
int f(void)
{
static int all = 1;
printf("in %s all=%d\n", __func__, all);
all += 2;
printf("agn in %s all=%d\n", __func__, all);
return all;
}
输出为:
in f all=1
agn in f all=3
in f all=3
agn in f all=5
in f all=5
agn in f all=7
5.3 全局变量
定义在函数外部,通常是程序的顶部。全局变量在整个程序的生命周期内都是有效的,在任意的函数内部都能访问全局变量。
即,全局变量在声明后在整个程序中都是可用的。
没有做初始化的全局变量会得到0值,指针会得到NULL值;它们的初始化发生在main函数之前。
#include /* 全局变量声明 */
int g;
int main ()
{
/* 局部变量声明 */
int a, b;
/* 实际初始化 */
a = 10;
b = 20;
g = a + b;
printf ("value of a = %d, b = %d and g = %d\n", a, b, g);
return 0;
}
在程序中,局部变量和全局变量的名称可以相同,但是在函数内,如果两个名字相同,会使用局部变量值。如下所示:
#include /* 全局变量声明 */
int g = 20;
int main ()
{
/* 局部变量声明 */
int g = 10;
printf ("value of g = %d\n", g);
return 0;
}
输出为:value of g = 10
tips:
返回本地变量的地址是危险的,返回全局变量或静态本地变量的地址是安全的;
不要使用全局变量来在函数间传递参数和结果;
使用全局变量和静态本地变量的函数是线程不安全的,所以尽量避免使用全局变量和静态本地变量。
6.学习数据类型
6.1 二进制负数
一个字节可以表达的数:0000 0000 - 1111 1111(0-255)
补码:
考虑-1,希望-1+1->0。如何做到?
0 -> 0000 0000 ; 1 -> 0000 0001
1111 1111 + 0000 0001 -> 10000 0000
结果多出了一个1,溢出不算。
因为 0-1 -> -1,所以 -1 = (1)0000 0000 - 0000 0001 -> 1111 1111
1111 1111被当作纯二进制看待时是255,被当作补码看待时是-1;同理,对于-a,其补码就是0-a,实际是2^n-a,n是这种类型的位数。
补码的意义就是拿补码和原码可以加出一个溢出的0。
数的范围:
对于一个字节(8位),可以表达的是:0000 0000 - 1111 1111;
其中,0000 0000 -> 0;
1111 1111 ~ 1000 0000 -> -1 ~-128;
0000 0001 ~ 0111 1111 -> 1~127。
所以,有如下的图来看加1减1时,数的变化。其中,逆时针表示的是加1,顺时针表示的是减1。
6.2 选择整数类型
没有特殊需要,就选择int。
现代的编译器一般会设计内存对齐,所以更短的类型实际在内存中也是占据一个int的大小,虽然sizeof告诉你更小。
unsigned与否只是输出不同,内部计算一样。
6.3 浮点的输入输出
如果没有特殊需要,只使用double。
6.4 运算符
优先级: