901 C语言学习记录帖

写在开始:电子信息 901c语言程序设计 学习内容及问题解决记录

目录

一、变量、常量及标准化输入输出

1.浮点数输入输出

2.字符串格式化输出

3.整数

二、运算表达式及优先级

1.&& > || 以及逻辑运算短路

2.a?b:c

3.k++

4.位运算符

三、循环结构

1.while(printf()"*")

2.continue & break

四、数组与字符串

1.数组

2.字符串函数

※3.排序算法

五、指针

1.优先级

六、函数

1.变量域

七、编程题总结

1.素数 isPrime()

2.文件相关操作

3.字符串相关操作

4.链表相关

八、选择题错误记录

8.5 错误

8.6 字符

8.7 预编译


一、变量、常量及标准化输入输出

1.浮点数输入输出

--输入scanf:

①double类型读入必须用%lf,否则无法进行。

②浮点数读入时,格式规定不能要求小数点后位数。

--输出 printf

①小数点形式输出 和 科学计数法输出,默认为小数点后六位,分别为****.123456 和 1.123456e+12,根据编译器不同,科学计数法也可能为1.123456e+123

②在对位数进行要求时,小数点、e、+ 、(+)后的数字 均算入位数。假设要求%m.nf 或 %m.ne,首先处理小数位数要求,科学计数法记得整数部分只能1位(1<=m<2),接下来按照格式补充e+12等位。此时,如果总位数 大于 m,则正常输出,不需要裁剪,若总位数小于m,则默认右对齐,左侧空格。

若有左对齐(-)要求,则左对齐,右侧空格;若需要补0,则在左侧空格处补0,左对齐则无法补,右侧仍空格。

空格、补0和符号:若要求显示正负号(+),符号应在空格后,若是补0情形,符号应在0前。

2.字符串格式化输出

形如:printf("%10.03s","****");

整数位:代表输出长度,若所截断长超过整数位,全部输出,不足则左侧补空格

小数位:代表从第0位开始,所截字符串长度

printf("%.4s\n","1234567890");
printf("%5.4s\n","1234567890");

 结果:  

1234
  1234

3.整数

--2.1两整数相除,向下取整(结果只取整数部分,而非四舍五入)。

--2.2 int类型强制转换,向下取整(结果只取整数部分,而非四舍五入)。

二、运算表达式及优先级

1.&& > || 以及逻辑运算短路

int a,b,c;
a=b=c=1;
++a||++b&&++c;

结果:a=2,b=1,c=1

分析:在逻辑运算中:!> && >||,可以理解为,在本题中,没有括号所以要按照优先级进行运算。&&优先级较高,所以先括起来,=》++a||(++b&&++c),此时,后进行运算的 || ++a=2,为+(逻辑值等于1),所以发生了短路,故本该先运算的&&以及其两侧,因为短路被省掉了。

短路现象中,通常是,本该先**再##,但短路发生后,导致**省略(可怜的优先级被忽视了)

2.a?b:c

int x=1,y=0;
printf("%d\n",x-->(y+x)?5:25>y++?'1':'2');
printf("x=%d,y=%d\n",x,y);

结果

5
x=0,y=0
分析:根据结果,以及个人调整代码数据发现,第一个y=0,第2个y=0,第一个x=1,第2个x=0。在条件运算符中,结合方向位从右向左。但是明显知道,所谓优先级低的左侧,其实未受右侧y++影响。

个人理解:优先级《=》在没有括号的时候,分析时可以自动加括号,然后就可以从左向右分析。考虑到++是一目运算符,>是二目运算符,高于三目运算符(条件运算符),所以变成了

x-->(y+x)?5:((25>y++)?'1':'2')

然后从左到右进行计算,发现走了5那一条,所以后面的不影响结果(相当于短路?)。

如果把括号里改成 y+x+1,则导致走 路2,然后先看括号里,y=0,25>0,y++(变成y=1),所以走路1,结果为‘1’,对应49  对应结果:49  x=0,y=1,与分析一致。

关于第2个y的值,通过调整25,变成 1时,结果仍为49,但变成0时,结果为50。故第2个y在能抵达的情况下,值为0。理解没有问题。

P.S.将此处优先级理解带入 逻辑运算符短路中,如上 1.&& > || 以及逻辑运算短路 中,++a||++b&&++c 可以变成 (++a) || ( (++b) && (++c) ),然后从左侧看,括号1,或运算,若括号1为真,则不需要看右边,若为假,则判断右侧括号,需要先看++b,若++b为假,则不需要右边,若为真,则看++c。这样理解可以通过。

总结:如红字。

3.k++

问题1:关于 int k=10; printf("%d\n",(k++)+(k++));结果输出。教材1 P34 认为,输出结果为10+10=20,k最终值为10+2=12。本人运行结果为 10+11=21.(参见下 问题2 尝试解释)

理解:教材2 P102指出,两种方案均可行,但准确来说,C语言标准并未定义结果应该是什么(与所使用编译器有关)。在P105 <副作用和序列点> part中表示,C语言保证程序执行下一条语句前,对 k递增两次,但未指明在本句中,是在子表达式求值后递增,还是所有表达式求值后再递增。

问题2:3+(k++)

在 自增/自减 外有括号时,并不影响其作用,如本问题,结果仍为 3+k,然后执行 k++。

-》在指针中 *(str++),同理,即:先取指针str所指值,然后指针向后移一位。

问题3:在printf中,存在以下状况:int u=3;

printf("%d,%d\n",u,u);     // 3,3

printf("%d,%d\n",u++,u); // 3,4

printf("%d,%d\n",u,u++); // 4,3

printf("%d,%d\n",++u,u); // 4,4

printf("%d,%d\n",u,++u); // 4,4

printf("%d,%d\n",++u,u++); // 5,3

printf("%d,%d,%d\n",u,u++,++u); // 5,4,5

printf("%d\t%d\t%d\t%d\t%d\t",u++,u,u++,++u,++u);//6       7       5       7       7

尝试性总结:

1.u,++u 的值总是变化之后的,无论其位置,均为递增最后的u

2.u++ 的值 从左往右递减,依次为(设最终u=n):n-1, n-2, n-3 ...

尝试性解释:

无,据说与编译器有关,其实我也没想出个解释 

浑说一个:u++其实相当于 u+1,输出 (u+1)-1,理解为退一。编译器用变量 m=0 记录u++数目,从左往右先读一边,遇到 ++(无论++u或u++),就m=m+1,然后把结果的m,若是u++,就告诉这个u++,记住啊你该退这个m位,遇到 ++u,就跟他说,你就是最终结果啊记住,遇到 u就说,你不就是u吗,最终结果也是u,那你就偷个懒,最终结果就是你了。所以。。。

我啥也不明白,如果再加上 - -,我可以表演一个劈叉

似乎也可以用,如下例子:(我简直是天才谢谢)

printf("%d\t%d\t%d\t%d\t%d\t",u++,u--,u--,u++,++u);//3       4       5       4       4

此处用m,退一 为-1,增一为+1,且会累计;

u++u--u--u++++u
m变化-1+1-1+1-1
赋予m值-1010-1
结果3=4+(-1)4=4+(0)5=4+(1)4=4+(0)4

printf("%d\t%d\t%d\t%d\t%d\t%d\t%d\n",++u,++u,u,u++,u++,++u,++u); //9  9  9  6  5  9  9

++u++uuu++u++++u++u
m变化-1-10-1-1-1-1
赋予m值-1-2-2-3-4-5-6
结果9996=9+(-3)5=9+(-4)99

经printf("%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",++u,++u,u,u++,u--,++u,++u,u++);//8 8 8 5 6 8 8 3验证可行

4.位运算符

运算符含义运算规则示例
~按位取反1变0,0变1~1001=0110

&

按位与同1则1,有0为01&0=0
|按位或同0则0,有1为10011 | 1001 = 1011
^按位异或同则为0,不同为1

0011^1001=0001

三、循环结构

1.while(printf()"*")

printf 函数的返回值是输出字符的个数。

题目:下面程序的循环控制表达式是否与‘0’等价

     int t=0;
	 while(printf("*"))
	 {
	 	t++;
	 	if(t>3) break;
	  } 

解释:printf 输出1 个字符,返回值为1,其在while中,对应逻辑值,为1。’0‘对应的ASCII值为80≠0,故其对应的逻辑值,也为1,所以等价。此外,还与 ’1‘,’2‘...等价。

2.continue & break

--switch语句

continue:实际作用于循环语句,而非控制switch;本轮switch到此结束,然后移到switch,改变循环变量后再选择一次。

break:作用于switch,结束所在switch过程

3.

四、数组与字符串

1.数组

1.1- 10*0正确与否

数组定义:int a[10]={10*0}; 是正确的,相当于 int a[10]={0};

1.2- char s[5]=""; 正确与否

正确。该 s 相当于 {'\0','\0', '\0','\0', '\0'}

1.3- 错误代码段修正。

//错误代码	
char a[6],b[]="China";
a=b;
printf("%s\n",a);

//修正后代码
char b[]="China";
char *a=b;
printf("%s\n",a);
//
int i;
for(i=0;i<9;i++){
    printf("%c:%d\n",a[i],a[i]);
}

修正后代码结果:

China
C:67
h:104
i:105
n:110
a:97
 :0
 :0
 :0
+:43

总结:c语言的巧妙在于指针的灵活运用,指针可以作数组理解,数组名也可以作指针理解。此处代码,将数组名作为指针,因此指针指向的内存区域,大小等于数组一位的大小,指针加一就等于数组的下标加一。指针问题最需要弄清两个问题:1是指针指向哪里 2是指针指向的是多大的内存区域(这样指针在移动的时候能明确移动的位置大小,或者读取的时候才知道要读多长)

2.字符串函数

总结:1.字符串名代表其首项地址,字符串函数所处理的是 从字符串首项 到 第一个\0 的字符段,如 puts,strlen,strlwr,strupr

2.c语言的字符串,代表的是从首项地址开始,对 此及后面的存储空间 按照数据类型划分小格,所以不存在溢出,使用printf可以限制,使用puts可以清楚看到 超出规定长度 的完整字符串,如strcat,strcpy。

3.在连接strcat中,会删去前一个字符串的\0,连接后在最后安上 \0。在复制粘贴strcpy中,会将新字符串粘进去,后接\0,若原字符串更长,后面会存在未影响部分。

4.影响了就是影响了,没影响的地方就是保留原样。影响字符串后接上\0,只使用puts就啥也看不到了(粉饰太平hhhh)。

2.1- gets & scanf 

①gets:格式 gets(char[ ]),形如 gets(s1); 每次只能从终端获取一个字符串。以回车作为结束。

②scanf::格式scanf("%s%s",char[ ],char[ ]),形如scanf("%s%s",s1,s2);每次可以从终端获取一个或多个字符串,串之间以空格结束(测试时 回车也可以)。(不用加&,因为数组名称天生代表地址)

③scanf 后面接 gets:如

aa bb <CR>cc dd<CR>

scanf("%s%s",a1,a2);
gets(a3);gets(a4);

其中 scanf 依靠空格和回车划分,所以 a1=”aa“,a2="bb",gets从scanf结束开始,到后面第一个回车结束,截取不含回车的一部分,故a3=”“,若bb后有空格,则a3=”□“,a4从a3后继续,到下一个回车结束,截取不含回车部分,a4=”cc dd“ 。

上述逻辑经验证可行。若先用gets,且输入回车,则与a3同样情况,得到空数组。

2.2- puts & printf

①puts

	char s1[]="ab\0ef\0";
	int i;
	for(i=0;i<6;i++){
		printf("%d\t",s1[i]);
	} 
	printf("\n");
	puts(s1);
	puts(s1);

结果:

97      98      0       101     102     0
ab
ab

总结:1.给字符数组赋值时,从 数组名所代表的地址 开始依次 把字符串所有字符赋入,

2.使用for循环输出,实际上不会考虑数组实际长度,不会报溢出错误,所以 ’\0' 不会影响继续输出。

3.使用puts,即从 数组名所代表的地址 开始,到 '\0' 止

4.puts会自动换行。无论后面还有没有输出,光标都会下移一行,若此后无输出,将会出现一行空行。

②printf

printf("%s",s1),效果与 puts 同

for循环结合printf("%c",s1[i]); 效果受for循环上限限制

2.3- strcat 

将字符数组2中的字符串连接到字符数组1的字符串后面

    int i; 
    char s1[20]="I want to ";//len=10
    char s2[]="study ";//len=6
    strcat(s1,s2);
	for(i=0;i<16;i++){//s1效果 
		printf("%c",s1[i]);
	} 
	printf("\n"); 
	for(i=0;i<10;i++){//s2是否存在 
	    printf("%c",s2[i]);
    }
    printf("\n"); 
    strcat(s1,s2);
    for(i=0;i<20;i++){//s1溢出效果 
	    printf("%c",s1[i]);
    }
    for(i=0;i<25;i++){//s1溢出效果 
	    printf("%c",s1[i]);
    }
    printf("\n");
    puts(s1);

结果:(乱码不要在意,那是没定义的地方)

I want to study
study   Y%
I want to study stud
I want to study study  
I want to study study

总结:1.相当于将 字符数组2 复制到 字符数组1末尾,对2无影响。(如结果 行1,2)

2.c语言中,数组长度并不是限制。所以s1不够大时,仍可以将整个s2复制进去,但用for打印会受限,用puts打印时,就可以看到完整的s2了。(如结果 行3,4,5)

2.4- strcpy

将字符串2复制到字符数组1。

    int i;
    char s1[20]="little";//len=10
    char s2[]="tend";
    char s3[]="dictionary";
    strcpy(s1,s2);
    for(i=0;i<15;i++){//短字符串 复制 
	    printf("%c",s1[i]);
    }
    printf("\n");
    printf("s1[4]=%d\n",s1[4]);
    strcpy(s1,s3); 
	for(i=0;i<15;i++){//长字符串 复制 
	    printf("%c",s1[i]);
    }
    
    s2[2]='\0';//s2中有\0
	strcpy(s1,s2);
	for(i=0;i<15;i++){//字符串中有\0
	    printf("%c",s1[i]);
    }

结果:

tend e
s1[4]=0
tend
dictionary
dictionary
te tionary

总结:1.用 比原字符串短 的字符串,则结果中,短字符串从开始依次替换对应位,以\0结束(若使用puts,则只显示短字符串)。若有未被替换的原字符串字符,不做处理。(如结果 行1,2,3)

2.原字符串中有没有\0不影响复制进去,也许会影响puts。s2中若有\0,会影响复制的部分(即会复制 开头至\0,然后正常粘贴进去)(如结果 行6)。

3.用 更长的字符串,则完全替换成新字符串。(如结果 行4,5)

规则:

复制和粘贴两步。

复制部分,从数组名对应的第一项位置开始,到\0结束。

粘贴部分,从数组2第一项开始,将复制部分 依次逐位 粘贴进去。

若比之前长,就相当于完全替换,若较之前短,则会出现 xxxx\0xxx\0情况,使用printf时取决于所定上限,使用puts时输出 新粘贴部分。

2.5- strlen

测试字符串长度。

    int i;
    char s1[]="ab\0ef\0";
    char s2[10]="asdf";
    char s3[10]={'2','3',};
    printf("%d,%d,%d\n",strlen(s1),strlen(s2),strlen(s3));

结果:2,4,2

总结:从输入的数组开始,到\0结束,测算有效字符的长度,具体字符串 如s1 占几位,并不重要。

2.6- strcmp

    char a[]="STOP",b[]="STOP ";
    printf("%d\n",strcmp(a,b));

 结果:1

总结:逐位比较,若出现不同字符,则比较字符的大小(ASCII码);若全部相同,则认为相等;若一个结束,另一个未结束(如上例),ASCII值最小为0,对应 '\0',所以先结束的小。

2.7- strupr

    int i;
    char s1[]="ab\0ef\0";
    strupr(s1);
    for(i=0;i<6;i++){//字符串中有\0
	    printf("%c",s1[i]);
	}

结果:AB ef

总结:只处理 从第一项s1[0]位置至\0段的有效字符。

※3.排序算法

选择排序,冒泡排序,插入排序。

3.1- 选择排序

原理解释:

利用每次 遍历 i 位到最后,找到最小位,如果最小位不是 i 位,则交换两位的值。

代码展示:(从大到小排序)

(1)数组做形参(用 int i , j 遍历)

void sort(int a[],int n)
{
    int mark,temp,i,j;
    for(i=0;i<n-1;i++)
    {
        mark=i;
        for(j=i+1;j<n;j++)
        {
            if(a[mark]<a[j]) mark=j;
        }

        if(i!=mark)
        {
            temp=a[i];
            a[i]=a[mark];
            a[mark]=temp;
        }
    }
}

(2)指针做形参(用指针遍历)

【当然,指针做形参也可以如(1) 用 int i , j 遍历,会更方便,本例供理解】

void sort(int *a,int n)
{
    int *mark,*temp,*i,*j; //i,j是指针,移动指针遍历
    temp=(int *)malloc(sizeof(int));
    for(i=a;i<(a+n-1);i++)
    {
        mark=i;
        for(j=i+1;j<(a+n);j++)
        {
            if(*mark<*j) mark=j;
        }
        
        if(i!=mark)
        {
            *temp=*i;
            *i=*mark;
            *mark=*temp;
        }
    }
}

(3)链表做形参(用指针遍历)

struct node{
    int num;
    struct node *next;
};

void sort(struct node *head)
{
    struct node *mark,*temp,*p,*q;
    //mark:指向最小值节点;p:每次要排好序的节点;q:用来和mark结合,进行遍历比较

    temp=(struct node *)malloc(sizeof(struct node));
    for(p=head;p->next!=NULL;p=p->next)
    {
        mark=p;
        for(q=p->next;q!=NULL;q=q->next)
        {
            if(mark->num<q->num) mark=q;
        }
        
        if(mark!=p)//如果最小值不是p所指节点,则交换两节点内整数值
        {
            temp->a=p->a;
            p->a=mark->a;
            mark->a=temp->a;
        }
    }
}

3.2- 冒泡排序

原理解释:

利用 i 对排好序的位置计数,利用 j 对未排好序的元素,依次比较相邻元素,并将未按顺序的交换顺序,最终将最大/小换到最后一位。

代码展示:

(1)指针做形参 (用 int i , j 遍历)

void sort(int *a,int n){
    int i,j,temp
    for(i=1;i<n;i++){
        for(j=0;j<n-i;j++){
            if(*(a+j)>*(a+j+1)){
                temp=*(a+j);*(a+j)=*(a+j+1);*(a+j+1)=temp;
            }
        }
    }
}    

(2)数组做形参(用 int i , j 遍历)

void sort(float a[],int n){
	int i,j;float t;
	for(i=1;i<n;i++){
		for(j=0;j<n-i;j++){
			if(a[j]>a[j+1]){
                f=a[j];a[j]=a[j+1];a[j+1]=f;
			}
		}
	}
} 

冒泡排序注:

1.只需要换好 n-1 位即可,所以 i 为1~(n-1)

2.由于是 j 与 j+1比较交换,所以第一轮最后一个是 n-2位,所以 j<n-i

3.根据实际排序元素,注意更改 数组/指针 a 的类型

3.2.1-双向冒泡排序

原理解释:

普通版使用 i 和 n-i 标识两端,i<n 标识排序进程,双向冒泡使用 low和high 在两端,low<high标识进度

代码展示:

void BiBubbleSort(int a[],int n)
{
	int low=0,high=n-1,flag,i,temp;
	while(low<high)
	{
		flag=0;
		for(i=low;i<high;i++)//正向冒泡,从low开始,最后到high-1与high相比较
		{
			if(a[i]>a[i+1])
			{
				temp=a[i];a[i]=a[i+1];a[i+1]=temp;
				flag=1;
			}	
		}
		high--;//正向结束,high上是最大值,high-1
		
		if(flag!=1) break;//遍历一遍,发现没有需要交换,说明排序成功,故结束
		
		for(i=high;i>low;i--)//反向冒泡,从high开始,最后到low与low+1相比较
		{
			if(a[i]<a[i-1])
			{
				temp=a[i];a[i]=a[i-1];a[i-1]=temp;
				flag=0;
			}
		}
		low++;//正向结束,low上是最小值,low+1
	}
 }

3.3- 插入排序

原理解释:

逐次对 1~n-1位元素进行插入,使用 temp保存当前要插入值

从i-1位开始判断,将大于的后移,一直到0位或者不再大于当前值,最后在所有大于的元素和小于的元素间空出一位,填入temp。循环停止时,将在目标位之前(不满足条件位),所以需要加一再赋值。

代码展示:

void sort(int *a,int n){
	int i,j,temp;
	for(i=1;i<n;i++){//从1开始,与前面比较
		temp=a[i];
		for(j=i-1;j>=0&&a[j]>temp;j--){//如果j位比当前位temp大,则需要后移,小于则停止
			a[j+1]=a[j];
		}
		a[j+1]=temp;//循环停止在小于temp位,j+1位原本大于,已被移走,当前为空
	}	
}

3.3.1 二分插入排序

与简单插入法相比,调整了寻找插入位置的方法,使用二分查找。减少比较次数。

void halfInsertSort(int a[],int len){
	int i,j,temp;
	int low,high,mid;
	for(i=1;i<len;i++){
		temp=a[i];
		low=0;high=i-1;
		while(low<=high){
			mid=(low+high)/2;
			if(a[mid]>temp)high=mid-1;
			else low=mid+1;
		}//其结果为 high (待插入temp) low 
		for(j=i-1,j>high;j--){
			a[j+1]=a[j];
		}
		a[high+1]=temp;
	}
} 
//最后移位也可以为 
/*
for(j=i-1;j>=low;j--){
	a[j+1]=a[j];
}
a[low]=temp
*/

3.4 二分查找法 (折半查找法)

//找到则返回对应下标,未找到返回 0 
int fun(int *a,int n,int m){//int数组 a,数组长度 n,待查找数字 m
	int low=0,high=n-1,mid;
	while(low<=high){
		mid=(low+high)/2;
		if(a[mid]>m){
			high=mid-1;
		}
		else if(a[mid]<m){
			low=mid+1;
		} 
		else{
			return mid;
		}
	}
	return -1;
} 

五、指针

1.指针与二维数组

规则:(注:此处 描述里的 a,代表 指针或者表达式;表达式里的a,代表二维数组)

① &a,即 取a的地址,取的是将a视作一个整块,其第一个字节的地址,即a的首地址。

②对于 无其他符号(如 *,&)影响的 a:若 a 指向数组,则 a指代 a数组的第一个元素的地址,即相当于 &a[0],a[0]相当于&a[0][0];若 a 指向元素,则 a指代a元素的值,如 a[2][3]。

③*a,与&符号将后面的元素看成整块不同,其是在a的含义基础上进行的取数。即若 a指向数组,则 a指代 数组a第一个元素地址,*a即为 数组a 第一个元素,如,*a 等价于 a[0] ,相当于 二维数组a第0行的数组 的数组名,代表 二维数组a第0行的首地址;对于 *a[0],由前句分析,a[0]是一维数组名,指代数组第一个元素地址,所以*a[0]取该数组的第一个元素,相当于a[0][0];若 a代表元素,则不能使用 *。

&a二维数组a的首地址
a,&a[0],&(*a),二维数组第0行首地址
a[0],a[0]+0,*a,*(a+0)+0,&a[0][0],

二维数组第0行第0个元素地址

*(a[0]),*(a[0]+0),*(*a),*(*(a+0)),a[0][0],

二维数组第0行第0个元素值

                    (注:此处元素代表最小单位 如int型,char型)

五、指针

1.优先级

1.1 *(ptr++)  V.S.(*ptr)++

问题1-  *(ptr++) 

本语句作用为:取ptr指针所指的值,指针ptr+1。以 自减 -- 为例如下:

    int x[]={1,3,5,7,9,11},*ptr;
	ptr=x;
	printf("%d\t",*(ptr--));
	ptr=x;
	printf("%d\t",*ptr--);
	ptr=x+2;
	printf("%d\t",*(--ptr));
	ptr=x+2;
	printf("%d\t",*--ptr);

结果:1       1       3       3

分析,对于自增/自减来说,加不加括号并不影响,不仅是指针,普通运算同样不影响,如 int i=1; 3+(i++)的结果为4 【本处 同 \\二、指针\\3.k++\\问题2】

问题2- (*ptr)++

本语句作用为:取指针 ptr所指的值,然后将ptr所指位上的值 自增,示例如下:

    int x[]={1,3,5,7,9,11},*ptr;
	ptr=x+2;
	printf("%d\t",(*ptr)--);
	printf("%d\t",ptr-x);
	printf("%d\t",*ptr);

结果:5       2       4

分析:第一个语句,取 x[2]值,并使x[2]=x[2]-1;根据第2个语句,可知,指针未移动;根据第3个语句可知,x[2]减少1

2.字符数组 V.S. 字符指针

前言:一般来说,字符数组名就是字符指针名,通常都能用,但 整体赋值时不同

字符数组字符指针
scanf("%s",str)×
str = "asdf"初始化√,赋值×

定义 char *s=“asdf”,字符串s为常量,不能修改

六、函数

1.变量域

书本p109

1.2 "a\0\04r+045\'b"

七、编程题总结

1.素数 isPrime()

int isPrime(int n){
	int i,isP=1;
	for(i=2;i<=n/2;i++){
		if(n%i==0) 
		{isP=0;break;}
	}
	
	return isP;
} 

注:为了统一路径return,定义了isP,也可以不用,分两次 返回 0 和 1

2.文件相关操作

2.1文件打开

if(fp=open("text.dat","r")==Null) {
	printf("cannot open file!\n");exit(0);
}

2.2 二进制文件读入结构体 - fread(buffer, size ,count, fp)

//本段只读一个
struct rec r;
while(!feof(fp)){//非文件尾判断
	fread(&r,sizeof(struct rec),1,fp);
}

2.3 结构体写入二进制文件 - fwrite(buffer,size,count,fp)

//结构体数组 写入文件
//struct student stud[10];
for(i=0;i<n;i++){
	if(fwrite(&stud[i],sizeof(struct student),1,fp)!=1){
		printf("File-writing error!\n");
	}
}

2.4 文本文件读入结构体 - fscanf(fp,"%s%d%c",name,&n,&a)

//本段读入一个
while(feof(fp)==0){
	fscanf(fp,"%s%d%c",name,&n,&a);
} 

2.5 结构体写入文本文件 - fwrite(fp,"%s%d%c",name,n,a)

//书上未找到,仿写结构体写入二进制
for(i=0;i<n;i++){
	if(fprintf(fp,"%s%d%c",name,n,a)!=1){
		printf("File-writing error\n");
	}
} 

2.6文本文件逆序输入(递归方法)

void reverse(void){
	char ah2;
	if(!feof(file2)&&(ch2=fgetc(file2)!='\n')){//如果file2未到尾,且当前ch2不是'\n' 
		reverse();
		fputc(ch2,file1);
	}
} 

3.字符串相关操作

3.1子串在母串中出现次数

int count(char *str,char *substr){
	int i,j,k,num=0;
    for(i=0;i<strlen(str);i++){
    	for(j=i,k=0;str[j]==substr[k];j++,k++){ //j遍历str,k遍历substr,对应项相等
    		if(substr[k]=='\n'){//对应项相等,保持到substr尾,则说明有一个子串
    			num++;break;
			}
		}
	}
	return num;
} 

3.2 字符串长度(strlen),字符串大小(strcmp),字符串复制(strcpy)

//strlen
char *s="asdf";
int count=0;
while(*a!='\0'){
	count ++;
}

//strcmp
int strcmp(char *a,char *b){
	for(;*a==*b;){
		if(*b=='\0') return 0;
		a++;b++;
	}
	return(*a-*b)
}

//strcpy
void strcpy(char *a,char *b){
	while((*a=*b)!='\0'){
		a++;b++;
	}
} 

3.3 不改变串的内容逆序输出串--递归

void inverp(char *a){
	if(*a!='\0'){
		inverp(a+1);
	}
	putchar(*a);
} 

3.4 删除字符串中所有空格

//本代码思路不是删除空格,而是复制所有非空格,最后strcpy复制回原串
#include <string.h>
#include <ctype.h>
void delspace(char *p){
	int i,t;
	char c[80];
	for(i=0,t=0;p[i];i++){
		if(!isspace(p[i])) c[t++]=p[i];//不是空格就复制 
	}
	strcpy(p,c);
}

4.链表相关

4.1 对所给数组 int a[] 构造链表

struct node{
	int num;
	struct node * next;
}; 

struct node * creat(int a[],int n){
	struct node *head,*rear,*p;
	rear = head=NULL;
	int i
	
	if(n<0) break;
	
	//构造头节点 
	p=(struct node*)malloc(sizeof(struct node));
	p->num=a[0];p->next=NULL;
	head=rear=p;
	
	//后续节点 
	for(i=1;i<n;i++){
		p=(struct node*)malloc(sizeof(struct node));
		p->num=a[i];
		p->next=rear->next;//给新节点赋值 
		rear->next=p;
		rear=p;//新节点接入,移动尾节点 
	}
	
	return head; 
}

4.2 循环链表之选猴王

//选猴王 --循环链表
int selectking(linklist *head,int c){
	linklist *p,*q;
	int i=0;
	do{
		i++;
		if(i%c!=0){
			q=p;
			p=p->next;
		}
		else{
			q->next=p->next;
			i=0;
		}
	}while(p->next!=p);
	
	return p->data;
}

4.3 有序链表插入新节点

//有序链表插入新节点--我自己写的,没有检验对错
void(linklist *head,int num){
	linklist *p,*temp;
	p=head;
	*temp=(linklist *)malloc(sizeof(linklist));
	temp->data=num;
	if(head==NULL){
		head=temp;
	}
	else if(num<head->data){
		temp->next=head;
		head=temp;
	}
	else{
		while(p->next!=NULL){
			if(num>p->next->data){
				p=p->next;
			}
			else if(num>p->data&&num<p->next->data){
				temp->next=p->next;
				p->next=temp;
			}
		}
	}
}

4.4 两有序链表合并,并删除相同值

//将两有序链表融合,并删除相同节点。
linklist * engage(linklist *head1,linklist *head2){
	linklist *p1,*p2,*p,*head;//p1遍历链表1 p2遍历链表2 p遍历结果链表 head结果链表头 
	p1=head1;
	p2=head2;
	
	if(p1==NULL){
		head=head2;
	}
	else if(p2==NULL){
		head=head1;
	}
	
	if(p1->data<p2->data){
		head=p1;
		p1=p1->next; 
	}
	else{
		head=p2;
		p2=p2->next;
	}
	p=head;
	
	while(p1!=NULL&&p2!=NULL){
		if(p1->data<p2->data){
			p->next=p1;
			p=p->next;
			p1=p1->next;
		}
		else{
			p->next=p2;
			p=p->next;
			p2=p2->next;
		}
	}
	
	if(p1==NULL){
		p->next=p2;
	}else{
		p->next=p1;
	}
	return head;
} 

八、选择题错误记录

8.1 字节数

char 1;int 2;long 4;float 4 ;double 8

int:-32768 至 32767 ;double读入用lf ;

8.2 赋值语句 运算符

e: 3e2,e之后必为整数;

%求余运算:两操作数必须为整型;

while(1<=x<=2):合法,等于(1<=x)<=2,其结果必为 1;

8.3 概念选择题

① sizeof

② #define double 3.14159 

③ 数组定义:类型说明符 数组名 [常量表达式] 

【常量表达式包括 常量 和 符号常量  如 4 和 #define m 3】

数组引用:下标可以是 常量表达式、变量或表达式,变量或表达式必须有确定的值。

④ 函数的形参和实参分别占用不同的存储单元

⑤c语言所有函数都是外部函数

-在c语言中,函数中变量的隐含存储类别是auto

-在c语言中,函数的隐含存储类别是 extern

⑥只有在使用才为该类型变量分配内存的存储类型说明:auto 和 register; static:编译时赋初值。

建议练习【函数】:第五章 函数_ - 百度文库 (baidu.com)

8.4 优先级

(1)普通运算符

一目:sizeof(),++,--,+正,-负,^

二目:&&>||

三目:a?b:c--从右往左

(2)指针相关

①*(pt+1)[2]:先算[ ],再算*;

②*p->b++: *((p->b)++):p->b=p->b+1,p指针所指的b内的地址加一,即后移一位。*取的是加之前的。其结果就是:取当前p对应当前b所指的值,b地址后移一位。

③++*p->b:b指针所指的值+1,且结果为+1之后的值

②③中p为结构体指针,a,b为结构体成员,b为char指针

④(*p).a: 成员运算符“.”优先级高于 *,故括号不能省;->也高于 *,如 *(++p)->b相当于*((++p)->b)

尝试性总结:*比其后面的运算要晚

8.5 错误

括号不配对:编译错误

8.6 字符

' '-32 (space); '0'-48; 'A' -65; 'a'-97

'\ddd'-八进制 '\xhh'-十六进制  printf("%o,%x",d,d);

'\b':退格(替换不是删除)printf("abc\b")--ab  printf("abc\b\n")--abc

8.7 预编译

① stdio.h

printf(); scanf(); FILE 【FILE *fp】; 

② string.h

puts(); gets(); str*()【strcat,strcpy,strcmp,strlen】

③ stdlib.h

malloc(); free(); exit(); 

8.8 元素定义

char a[ ]={0,1,2,3,4,5}; 合法

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值