首页 > 其他分享 ><<你必须知道的495个C语言问题>>

<<你必须知道的495个C语言问题>>

时间:2022-12-29 19:25:08浏览次数:45  
标签: main return int char printf include

title: <<你必须知道的495个C语言问题>>
categories: C书籍

一. 声明与初始化

我该用哪种类型

如果你定义明确的溢出特征,不想把正负号牵扯在内的话

如果你希望操作二进制位和字节时避免符号扩展

符号扩展

是在二进制位前面用1补全或者用0补全

指针声明

char* p1,p2;

我这样声明有什么问题?

组后得到的数据类型是p1位char指针,而p2是char整形

声明语法的解释

基本类型 生成基本类型的东西

而我们的*是一属于后者,于是不能出现在基本类型的符号里面

上面的例子改为

char *p1, *p2

类型定义typedef

defeine与typedef的区别确实是define只做一个字符串的替换..但是细微下来,好像又不是

typedef char *ptr1;
#define ptr2 char*;
ptr1 str1,str2;
ptr2 str3,str4;

我们定义了4个变量

str1,2,3都是指针变量,而str4却不是

原因

  1. 就是因为#define做了一个字符串的替换,并且因为类型声明的原因,str4不是指针
  2. tyedef真的可以把它后面的数据都定义为那一种数据

define也不是一无是处….defiene的作用很多.比如很多与define有关的命令

有关结构体和一般链表的声明

struct 
{
	int ID;
	char* Name;
	int arr[20];
}new_type;

这种声明只能用一次.然后这种声明就真的一无是处吗?????

用在typedef就很不错的效果

用typedef去定义链表

错误的情况

typedef struct 
{
	int ID;
	char *Name;
	student Next;//只是一个链表指针
}* student;//我们呢这里已经定义了一个新的结构体指针类型.名字叫student

于是编译器报错

​ [Error] 'student' does not name a type

他说我们没有定义那个类型‘student’

哦???

好像是的

因为我们以前都是把(int,char,char *,i nt *…)这些已知的数据类型来重新封装一下

对于上面提到的链表相关的typedef里面的成员类型student Next;就是一个我们之前没有的东西

那该怎么办

那就声明一个假的变量..就像你去图书馆..你可以人不在,,却可以拿上一本书去占位置

C0de

struct dqx;//事先准备好一个结构体标签
typedef struct dqx* Next_ptr;//也定义一个那样的结构体指针类型
struct dqx
{
    int ID;
    char *Name;
    Next_ptr Next;//这里直接使用
};
//这种写法不建议使用..就很绕..你懂吗.....

这样写好些

typedef struct dqx
{
    int ID;
    char *Name;
    struct dqx  *Next;//只是一个链表指针
} *Student;

还有一种写法

struct dqx
{
    int ID;
    char* NAme;
    struct dqx* next; 
};
typedef struct dqx* student_ptr; 
student_ptr ahead;
student_ptr behind; 

这样定义链表指针会更加的方便

闭环链表的typedef

这样写编译器会报错

typedef struct
{
    B_ptr b_pointer;	
}* A_ptr;
typedef struct
{
    A_ptr a_pointer;	
}* B_ptr;

[Error] 'B_ptr' does not name a type

还有一种写法也没有错,就是怪怪的

struct A
{
    struct B B_ptr;
};
struct B
{
    struct A* A_ptr; 
};

这样是不会报错的

其实我们也知道问题出在哪里,于是这么写之后……

#include<stdio.h>
#include<stdlib.h>
int main()
{
	struct A;
	struct B;
	typedef struct A *A_ptr;
	typedef struct B *B_ptr;
	struct A
	{
		int ID;
		B_ptr B_pointer;
	};
	struct B
	{
		int ID;
		A_ptr A_pointer;
	};
	
	A_ptr x_a=(A_ptr)calloc(1,sizeof(A_ptr));
	B_ptr y_b=(B_ptr)calloc(1,sizeof(B_ptr));
	
	x_a->ID=2001;
	y_b->ID=2022;
	printf("%d %d\n",x_a->ID,x_a->B_pointer->ID);
	printf("%d %d\n",y_b->A_pointer->ID,y_b->ID);
	free(x_a);
	free(y_b);
	
	
	return 0;
}
//我们用这个闭环链表去互相访问对方的数据

用typedef定义一个3维的指针

然后依次使用那个指针…

示意图

image-20221025005927107

#include<stdio.h>
#include<stdlib.h>
int main()
{
	typedef char *ptr1;
	typedef ptr1 *ptr2;
	typedef ptr2 *ptr3;
	
	//ptr3 arr0[2];  
	char*** arr0[2];//数组也是一个维度.所以是4维空间 
	ptr2 arr1[2];
	ptr2 arr2[2];

	ptr1 arr11[2];
	ptr1 arr12[2];
	
	ptr1 arr21[2];
	ptr1 arr22[2];

	arr0[0]=arr1;
	arr0[1]=arr2;
	
	arr1[0]=arr11;
	arr1[1]=arr12;
	
	arr2[0]=arr21;
	arr2[1]=arr22;

	char str[9]="ABCDEFGH";
	
	arr11[0]=&str[0];
	arr11[1]=&str[1];
	arr12[0]=&str[2];
	arr12[1]=&str[3];
	arr21[0]=&str[4];
	arr21[1]=&str[5];
	arr22[0]=&str[6];
	arr22[1]=&str[7];	
	
	
	printf("%c\n",*arr0[0][0][0]); 
	printf("%c\n",*arr0[0][0][1]);
	printf("%c\n",*arr0[0][1][0]);
	printf("%c\n",*arr0[0][1][1]);
	
	
	printf("%c\n",*(*(*(*(arr0+1)+0)+0))); 
	printf("%c\n",*(*(*(*(arr0+1)+0)+1))); 
	printf("%c\n",*(*(*(*(arr0+1)+1)+0)));
	printf("%c\n",*(*(*(*(arr0+1)+1)+1)));		
	return 0;
}


我们值得执意一下关于

	printf("%c\n",*(*(*(*(arr0+1)+0)+0))); 
	printf("%c\n",*(*(*(*(arr0+1)+0)+1))); 
	printf("%c\n",*(*(*(*(arr0+1)+1)+0)));
	printf("%c\n",*(*(*(*(arr0+1)+1)+1)));		

这种表达式

读不懂的代码还无法运行

#include<stdio.h>
#include<stdlib.h>

typedef int (*ptr_func)();//以及函数指针 
typedef ptr_func (*ptr_ptr_func)();//返回函数指针的函数指针 

ptr_func start(),stop();//简单的函数指针 
ptr_ptr_func state1(),state2(),state3(); //一些可以返回函数指针的函数 

void state_mac()
{
	ptr_ptr_func state=start;//声明一个函数指针变量 ,然后指向了start()函数 
	
	while (start != stop)
		state= (ptr_ptr_func)(*state)();//先是调用start	
}
ptr_func start()
{
	return (ptr_func)state1;//返回函数的地址 
}
int main()
{
	state_mac();
	return 0;
}
#include<stdio.h>
#include<stdlib.h>

struct func_thunk start(),stop();//定义了2个返回结构体的函数 
struct func_thunk state1(),state2(),state3(); 

struct func_thunk//这个结构体的成员只有一个,而且还是一个返回结构体的函数指针 
{
	struct func_thunk(*ptr_func)();
};


void state_mac()
{
	
	struct func_thunk state0={start}; //初始化结构体变量 
	while(state0.ptr_func!=stop) 
		state0=state0.ptr_func(); //
}
struct func_thunk start()
{
	struct func_thunk ret;
	ret.ptr_func=state1;
	return ret;
}
int main()
{
	state_mac();
	return 0;
}




数组大小

关于如何解决数组的大小的传递……现在还没有得到一个满意的答案

标识符的三大属性-避免命名错误

掌握这些规则的话….你就可以对一些二名字重复使用却不会报错

作用域

标识符有效的区域

命名空间

行标 label

也就是你要goto的地方

标签

tag,结构体,联合体枚举类型

结构体/联合体的成员

一般标识符

连接类型

外部连接

一些全局的./非静态的/一些函数

在所有的源文件中有效

(对于为什么是静态的变量,哪些函数我还不太明白)

内部连接

限制在该文件之下的静态变量与函数

无连接

(我们通常定义的那些)的局部变量….如typedef的类型定义

另外有一些关于类型标识符的特殊处理..让它躲避那些规则…详情就看书.P16

关于那些规则..我们要注意

  1. 不轻易使用以下划线为开头的指针

在C语言的一些库函数中也有一些特殊的宏定义,而不是库函数去处理字符..就像Python那样…P17

关于初始化

一些数据如果不去初始化而去使用,里面有很多的垃圾数据

关于动态内存分配

calloc可以初始化堆区的数据为0,但是对指着/浮点数的堆区就不会有用

1.34

对于

#include<stdio.h>
int main()
{
	char str1[20]="123456";
	return 0;	
}

对于这种情况….str1是字符串的第一位元素的指针.str1不需要任何操作就是字符串首个元素的指针

编译器也不报错

对于

#include<stdio.h>
int main()
{
	
	char *str2="7890";
	return 0;	
}

发出警告

[Warning] 不推荐将常量字符串指针转化为char*

什么意思?

在C语言中,一个字符串就可以代表它的地址,这个地址是首位元素的指针

char *str2="7890";它的寻址是str2寻求那个字符串的指针,然后实现一个对接..于是就可以read访问其中的元素

关于上面提及的问题

#include<stdio.h>
int main()
{
	printf("%c%c%c","dqx"[0],"dqx"[1],"dqx"[2]);
	return 0;	
}
//不会报错,正常执行...输出dqx

一个字符串它会返回首位元素的地址,这个字符串在哪?通常在一个常量区

所以才有那个警告…一个没有用数组方式定义的字符串更像是没有名字的野孩子,还不可调教

发出警告

[Warning] 不推荐将常量字符串指针转化为char*

不推荐把字符串常量(const char * )换为char *

所以

#include<stdio.h>
int main()
{
	const char* str="dqx";
	return 0;	
}
//正常编译不会出错

既然是常量区

所以

#include<stdio.h>
int main()
{
	char* str1="dqx";//是否加const都一样
	str1[1]='x';
	return 0;	
}
//在编译时,只是发生警告罢了....运行时会停顿一下发生斩断

这个程序在调试时会发生崩溃…….字符串在常量区,,而你却修改它…发生了冲突

于是报错“说你访问了一块不可修改的内存区域”

二. 结构体,联合.枚举

struct x1 {...};
typedef struct {...} x2;

他们的区别在哪?

x1在使用时更是需要引出struct,告诉编译器这是一个结构体(在.c文件是这样的.cpp可以不用)

x2更加的抽象,,,使用时不需要用structu

typedef struct x
{
int ID;
char *name;
}x;

这样写编译器会报错吗??

前面我们已经对这个问题做了大概的了解,,,这样写没有错..但是不怎么方便使用

数组加长

这个只是静态的数组加长

在动态输入的时候仍然不可以实现…..

可能还是要链表吧?

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct name
{
	int len;
	char str[1];
};
struct name *make_name(char*new_name)//一个接受字符串.返回结构体指针的函数 
{
	struct name *ret=(struct name *)malloc(sizeof(struct name)-1+strlen(new_name)+1);//把数组的 str[1]取代,换上len+1的长度 
	if(ret!=NULL)//为什么这个结构体可以修改长度..因为他在堆区呀... 
	{
		ret->len=strlen(new_name);
		strcpy(ret->str,new_name);
	}  
	return ret;//这个S指针的指向值 长度与D指针指向的长度完全不一样... 
}

int main()
{
	char x1[]="dqx"; 
	char x2[]="ywj";
	struct name *n1= make_name(x1);//其实的话..好比一个int*指针可以接受一个long long*的指针吗?似乎不可以..但这里也是一个意思.. 
	struct name *n2= make_name(x2);
	puts(n1->str);
	puts(n2->str); 
	free(n1);
	free(n2);
	return 0;
}
//引发了我的指针的思考.....指针类型的相互转化 
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct name
{
	int len;
	char *str;
};
struct name *make_name(char*new_name)//一个接受字符串.返回结构体指针的函数 
{
	int new_len=strlen(new_name);
	int struct_len=sizeof(struct name);
	char *buff=(char*)malloc(struct_len+new_len+1); 
	struct name *ret=(struct name *)buff;
	ret->len=new_len;
	ret->str=buff+struct_len;
	strcpy(ret->str,new_name); 
	return ret; 
}

int main()
{
	char x1[]="dqx";
	struct name *n1= make_name(x1);//其实的话..好比一个int*指针可以接受一个long long*的指针吗?似乎不可以..但这里也是一个意思.. 
	puts(n1->str);
	free(n1);
	return 0;
}
//引发了我的指针的思考.....指针类型的相互转化 

三. 表达式

有些值既是左值又是右值

#include<stdio.h>
int main()
{
	int arr[10]={9,9,9,9,9,};
	int i=0;
	arr[i]=i++; 
	printf("%d\n",arr[0]);
    printf("%d\n",arr[1]);
	return 0;
} 

所以你的结果是什么?

不太的编译器会给出不太的答案

Mingw

0
9
请按任意键继续. . .

GCC

9
0

--------------------------------
Process exited after 0.009451 seconds with return value 0
请按任意键继续. . .

对于

#include<stdio.h>
int main()
{
	int sb=2;
	printf("%d\n",sb++*sb++);
	printf("%d\n",sb++*sb++);
	return 0;
} 

Mingw

4
16
请按任意键继续. . .

GCC

6
20

--------------------------------
Process exited after 0.01203 seconds with return value 0
请按任意键继续. . .

#include<stdio.h>
int main()
{
	int sb=10;
	sb=sb++;
	printf("%d\n",sb);
	return 0;
} 

在我这里2个答案都是10….woc,,,

逻辑判断符的短路效应

通俗的讲就是..如果判断一部分就知道整体了..那么就不会在进行判断了

对于&,|,!通常会把所有的条件给判断个遍…

下面这个Code就hi让程序崩溃

#include<stdio.h>


int main()
{
	int x=10,y=0,z=5;
	int* p=NULL; 
	if(y!=0&x/y>z)//0不能作为除数
		puts("yes");
	else
		puts("no");
		
	if(p==NULL|*p=='\0')//NULL的这内存区域是不可读取不可写入的
		puts("yes");
	else
		puts("no"); 
	return 0; 
} 

如果你用&&,||,!的话,就不会存在上面的系统崩溃

关于i++与++i

在调用最后的结果时..就就是说i++与++i最后的结果都是一样的

在掉用当时的结果时,也就是说a=i++与a++i会有所不同

运算时的类型转化

#include<stdio.h>
int main()
{
	int a=1000,b=1000;
	long int c=a*b;
	printf("%ld",c);
	return 0; 
} 

这个表达式的意思是…..

int x int
然后将结果转化为long
再赋值给long
//这里的int x int 这里的结果很可能超过了long int的范围.....有漏斗
#include<stdio.h>
int main()
{
	int a=1000,b=1000;
	long int c=(long)a*b;
	printf("%ld",c);
	return 0; 
} 

这个代码的不同之处…..a是被显示的转化为long类型,在计算时,b被隐式的转化为long类型,于是得到的数据会<=long_max*long-max

这个代码等价于

#include<stdio.h>
int main()
{
	int a=1000,b=1000;
	long int c=(long)a*(long)b;
	printf("%ld",c);
	return 0; 
} 

但是有一种很蠢得写法

#include<stdio.h>
int main()
{
	int a=1000,b=1000;
	long int c=(long)(a*b);
	printf("%ld",c);
	return 0; 
} 

写了就像没有写一样…(a*b)本来就会隐式的转化为long类型,,无法提前阻止危险的发生

好比下面的代码也是这样

#include<stdio.h>
int main()
{
	double x=0,y=34;
	x=(double)(5/9)*(y-32); 
	printf("%lf",x); 
	return 0; 
} 

三目运算符的返回值利用

通常情况下我们的三目运算符会返回一个值,而不是变量..我们却可以用指针去模拟它

#include<stdio.h>
int main()
{
	
	int a=20;
	int b=30; 
	*(a>25?(&a):(&b))=100;
	printf("a:%02d b:%02d	\n",a,b); 
	  
	return 0; 
} 

输出

a:20 b:100

--------------------------------
Process exited after 0.0157 seconds with return value 0
请按任意键继续. . .

若类型不一致,会转换成int比较

int 可以表示 char, unsigned char, short, unsigned short 的所有值

眼过千遍不如手过一遍! 书看千行不如手敲一行! 手敲千行不如单步一行! 单步源

大小不一致,故编译器选择了值保护规则

提升的类型和之前的无符号类型一样大,则和无符号保护规则一致。

#include <stdio.h>

int main()
{
			 char  c1 = 0x88;//c1本来就是负数 
	unsigned char uc1 = 1;
	
			 short  s1 = 0x8888;//本来就是负数 
	unsigned short us1 = 1;
	
			 int n1  = 0x88887777;//本来就是负数 
	unsigned int un1 = 0x81007777;//本来就是负数 
	
	if(c1>uc1)
		printf("%d>%d\n",c1,uc1);
	else
		printf("%d<%d\n",c1,uc1);
	if(s1>us1)
		printf("%d>%d\n",s1,us1);
	else
		printf("%d<%d\n",s1,us1);
	if(n1>un1)
		printf("%d>%d\n",n1,un1);
	else
		printf("%d<%d\n",n1,un1);
}

值保护与无符号保护

#include<stdio.h>
int main()
{
    unsigned short int a=10;
    int b=-5;
    if(b>a)
    	printf("%d>%d 无符号保护规则\n",b,a);
    else 
		printf("%d<%d 值保护规则\n",b,a);

    printf("int %d-----unsigned short int %d",sizeof(int),sizeof(unsigned short int));
    return 0;
}

对于有符号的int,long,long..4字节以及以上的类型

一个比它<它的无符号类型,,带正负比较大小

一个>=它的无符号类型,采用正数比较大小

对于有符号的short,.char 小于4字节以及以下的类型

一个比它<=它的无符号类型,,带正负比较大小

一个>它的无符号类型,采用正数比较大小

值保护规则:带有符号位的比较

无符号保护规则: 转化为正数比较

#include<stdio.h>
int main()
{
	unsigned char x=0x80;//127=0x7f
	unsigned long y=0;
	y|=x<<8;
	printf("0x %lx ",y);//你用%x也无所谓 
	return 0;
}

在这个代码中…x会被转化为unsigned long

最后输出0x8000

运算符优先级

过多的括号会引起难以读取,,掌握一定的顺序也有一定的好处

左上的优先级大

()、[ ]、->、 .(点)
!、~、++、--、type、*、&、sizeof、
*、 / 、 %
+、-、
<<、>>
<、<=、>、>=、
==、!=、
&、
^、
|、
&&、
||、
Exp1<Exp2 ? Exp3 : Exp4
,
=、+=、-=、*=、/=、%=、>>=、<<=

四. 指针

char* 访问int数据

#include<stdio.h>
int main()
{
	int arr[5]={'d','q','x','i','s'};
	char *p=NULL;
	int i=0;
	for(i=0;i<5;i++)
	{
		p=(char*)((int*)arr+i);//这里的int*有点多于
		puts(p);//arr+i之所以可以指向下一个单元,,,是因为它本来就是int*
	}
	return 0;
}

关于多级指针的实用

#include<stdio.h>
int* func(int**); 
int main()
{	
	int x=100;
	int *ip2=&x;
	int **ip1=&ip2;
	printf("%d\n",**ip1); 
	return 0;
}

错误的用法是

	int *ip2=&x;
	int **ip1=ip2;

这里让ip1的地址直接指向了x而不是ip2

通过函数修改指向,而不是指向的值//

#include<stdio.h> 
void f(int** ipp)
{
	static int y=8;
	*ipp=&y; 
}
int main()
{
	int a=4;
	int *ip=&a;	
	printf("%08X -> %d\n",ip,*ip);
	f(&ip);
	printf("%08X -> %d",ip,*ip);
	return 0;
}

通过函数的形式修改指向

一般的话..我们修改指针的指向

int a=10;
int b=20;
int *ptr=&a;//这里的指向是a
ptr=&b;//这里的指向是b

那么我们这么拖过传入的参数去进行一个指向的修改???

用到二级指针的概率

方式一

函数修改指针

#include<stdio.h> 
void f(int** ipp)//所以我们这里做了一个二级指着..另外ipp也只不过是一个数据的拷贝 
{
	static int y=8;//我们无法修改ipp的值,就像我们传递进来一个数据0x1234,我们不可能对数据0x1234进行一个修改 
	*ipp=&y; //我们却可以对*(0x1234)的指向进行一个修改 
}
int main()
{
	int a=4;
	int *ip=&a;	
	printf("%08X -> %d\n",ip,*ip);
	f(&ip);//我这里传递的就是一个指针的指针 ,ip是指针,我有吧指针的地址传进去了 
	printf("%08X -> %d",ip,*ip);//所以调用了函数后我们就修改了ip的指向... 
	return 0;
}
//我们无法改变一个地址...但是我们可以更改指向....!!!你不可能把a=10当中a的地址做一个更改 
0062FE1C -> 4
00403010 -> 8
--------------------------------
Process exited after 0.01201 seconds with return value 0
请按任意键继续. . .

方式二

#include<stdio.h> 
int* f() 
{
	static int y=8;
	return &y;  
}
int main()
{
	int a=4;
	int *ip=&a;	
	printf("%08X -> %d\n",ip,*ip);
	ip=f();
	printf("%08X -> %d",ip,*ip);
	return 0;
}

0062FE14 -> 4
00403010 -> 8
--------------------------------
Process exited after 0.009186 seconds with return value 0
请按任意键继续. . .

五. 空指针

六. 数组与指针

首先数组!=指针…这你也知道…

如果你在一个

main.c文件中定义了

char str[100];

在othrt.c的文件中

你可以

extern char str[100];

但是不不能

char* str;

关于指针与数组的区别

浅浅的说一下

char *p1="dqx";
char p2[]="okj";

然后看到

p1是寻址一个地址,那个地址是“dqx”字符串的首地址

p2是不需要寻址,他就是“okj”的第一个元素的地址

这是与编译有关的原理

数组

空间分配是确定的…不能修改空间大小

它的地址是常量..不可修改

指向一块内存区的指针

可以作为数组使用….

空间大小可以人为的DIY..可以动态内存分配

它可以指向任意一块内存的地址

数组和指针的编译方式可能不同…

关于字符串数组的不能初始化

数组这个东西我们一般不能直接赋上一大坨值

#include<stdio.h> 
#include<string.h>
int main()
{
	char arr[10];
	//arr="dqx";//这会报错.....
	strcpy(arr,"dqx");//但是这样就可以..就离谱
	puts(arr);
}

但是我们这样做

#include<stdio.h> 
void init_str(char *);
int main()
{
	char arr[10]={0};
	printf("%08X\n",arr);
	init_str(arr);
	printf("%s",arr);
}
void init_str( char *str ) 
{
	printf("%08X\n",str);
	if(str!=NULL)
		str="dqx"; 
	else
		puts("emmmm");
	printf("%08X\n",str);
}

这一段代码看似没有啥问题..输出“dqx”

但是

假设arr=0x1234,str进来也是0x1234(事实如此)

然后str=“dqx”…

于是str=0x5678;

那么这就明白为什么啥也不输出了`

你传递过去的是一个地址

这个地址有变量来接受..在函数当中,,,,,,这个变量的地址又发生了变化

数组指针

它可以用数组的形式去遍历..然后用指针访问

指针数组貌似也是哈哈….

原理图

数组指针.png

(*ptr2)[0],好比这个….以数组[0]的形式找到那个指针..然后用(*ptr2)去访问

值得注意的是它是怎么用的

#include<stdio.h> 

int main()
{
	char str1[6]={'X','Y','Z','O','P','Q'};//地址 32-47
	char str2[6]={'G','H','I','J','K','Z'};//地址 16-31
	char str3[6]={'A','B','C','D','E','F'};//地址 0 -15
	
	char  *ptr1=str1;
	char (*ptr2)[6]=str3;
	
	
	printf("%c\n",*ptr1);
	ptr1++;//这里的+1是一个字节 
	printf("%c\n",*ptr1);

	//对数组指针
	printf("%c %c\n",(*ptr2)[0],(*ptr2)[1]);	
	ptr2+=3;//为什么我会+3..
	//这里的+3是+3*6=18; 
	 
 	printf("%c %c",(*ptr2)[0],(*ptr2)[1]); //注意运算符优先级地重点,,很容易出错 
	
	return 0; 
}

为什么我会ptr2+3

关于地址这个东西我写在上面的…..

ptr2的地址最小

然后str2,str3,str1他们的地址相差16,也就是一行数据段的空间大小

所以的话

ptr2+3代表ptr的字节位置移动了18个

移动到[15]代表遍历完str3

移动[16] ,[17] ,[18],就遍历到元素I

为什么sizeof不可以给出数动态内存数组的大小….

sizeof能够返回数组大小的情况,,,?

如果数组的大小未知或者已经退化为指针,,则它不能提供数组的大小

sizeof(char*)==8

siezof(inti*)==8

那些是指针的大小

动态内存分配多维数组

它的原理就是动态内存分配一个二维数组

big的大小也是动态分配…

big每个元素的指向也是动态内存分配的`

#include<stdio.h> 
#include<stdlib.h>
int main()
{
	int** big=(int**)malloc(sizeof(int*)*5); 
	int i=0,j=0,x='A';
	for(i=0;i<5;i++)
		big[i]=(int*)malloc(sizeof(int)*6); 
	
	for(i=0;i<5;i++)
		for(j=0;j<6;j++)
			big[i][j]=x++;
	
	for(i=0;i<30;i++)
			printf("%c ",*((*big)++));		
	
	return 0; 
}

这样做的话..导致动态内存分配的空空间不;连续

动态内存分配不连续

里面的最底层元素之间的间距是很大的,.也就是不连续

上面的每一个方框块都是分配出来的,,,,我们开辟了6个堆区..难以实现这几个堆区的地址连续衔接

输出乱的,

A B C D E F  T G H I J K L  T M N O P Q R  T S T U V W X
--------------------------------
Process exited after 0.03308 seconds with return value 0
请按任意键继续. . .

我们可以开辟2个堆区

堆区与堆区之间的地址不是连续的

但是堆区内部的单元是地址连续的

连续堆区

我么只是开辟了2个堆区

#include<stdio.h> 
#include<stdlib.h>
int main()
{
	int** ptr1=(int**)malloc(5*sizeof(int*));
	int* ptr2=(int*)malloc(5*10*sizeof(int));
	int** buff=ptr1; 
	int i=0,j=0,x='A';
	
	for(i=0;i<5;i++)
		ptr1[i]=ptr2+i*10;//让高阶堆区指向连续的地址 
		
	//初始化堆区	
	for(i=0;i<50;i++ )
			(*ptr1)[i]=x++;
			
	for(i=0;i<50;i++ )
		printf("%c ",*((*ptr1)++));
		//printf("%c ",(*ptr1)[i]);//也行 

	
	return 0; 
}

输出

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r
--------------------------------
Process exited after 0.01136 seconds with return value 0
请按任意键继续. . .

用动态内存分配一个三维正方体

#include<stdio.h>
#include<stdlib.h> 
void show(); 
int main()
{
	int x=3,y=3,z=4;
	char*** Cube=(char***)malloc(x*sizeof(char));
	int i,j,k,ascii='A';
	
	for(i=0;i<x;i++)
	{
		Cube[i]=(char**)malloc(y*sizeof(char*));
		for(j=0;j<y;j++)
		{
			Cube[i][j]=(char*)malloc(z*sizeof(char));
			for(k=0;k<z;k++)
				Cube[i][j][k]=ascii++;
		}
	}
	show(Cube); 
	return 0;
}
void show(char*** Cube)
{
	int x=3,y=3,z=4;
	int i,j,k;
	
	for(i=0;i<x;i++)
	{
		for(j=0;j<y;j++)
		{
			for(k=0;k<z;k++)
			{
				printf("%c ",Cube[i][j][k]);
			}
			free(Cube[i][j]);
			puts("");
		}
		free(Cube[i]);
		puts("");
	 }
}  

实现数组从1开始计数的操作

#include<stdio.h>
#include<stdlib.h> 
int main()
{
	int arr[10];
	int *ptr=&arr[-1];
	int  i=0;
	int ascii='A';
	
	//从小标为1的开始赋值 
	for(i=1;i<=10;i++)
		ptr[i]=ascii++;
		
	//我们试一下原来的数组是否导入 
	for(i=0;i<10;i++)
		printf("%c ",arr[i]);
	return 0;
}

输出

A B C D E F G H I J
--------------------------------
Process exited after 0.01377 seconds with return value 0
请按任意键继续. . .

七. 内存分配

这段代码写的有点做作…..

#include<stdio.h>
#include<stdlib.h> 
#include<string.h>

int main()
{
	char str[10],*ptr=NULL;
	fgets(str,10,stdin);
	ptr=strchr(str,'\n');//你的输入截断在换行那里
	if(ptr!=NULL) 
		//*ptr='\0';//手动设置空字符
	puts(str); 
	
	return 0;
}

这段代码他说我只是分配了一行的内存????

为什么运行没问题呀,,,3行输出

#include<stdio.h>
#include<stdlib.h> 
#include<string.h>

int main()
{
	FILE* fp=fopen("D:/in.txt","r");
	char linebuf[80];
	char* line[100];
	int i;
	for(i=0;i<100;i++)
	{
		char* p=fgets(linebuf,80,fp);
		if(p==NULL)
			break;
		line[i]=p;
		puts(line[i]);
	}
	fclose(fp);
	return 0;
}

局部变量返回的解决方案

首先关于一般的auto局部变量,他曾在栈里面,会被清空..所以它的地址我们不可以返回,它的数值可以

然后书上说不推荐使用static….它是静态数据不可写入,,,调用者还不能多次调用static所在的函数,否者会引起数据覆盖

推荐使用已经分配好的内存

如果用堆区记得释放

#include<stdio.h>
#include<stdlib.h> 
#include<string.h>
char* func1(int n);//wrong
char* func2(int n,char*);//safe
char* func3(int n);//static
char* func4(int n);//malloc

int main()
{
	int n=50;
	char *ptr1=NULL;
	char ptr2[10];
	char *ptr3=NULL;
	char *ptr4=NULL;
	
	//ptr1=func1(n);
	func2(n,ptr2);
	ptr3=func3(n);
	ptr4=func4(n);
	printf("%s %s %s ",ptr2,ptr3,ptr4);
	free(ptr4);
	return 0;
}
/*
char* func1(int n)
{
	char buff[10]={0};
	sprintf(buff,"%d",n);
	return buff;//函数的局部会被清空..不要返回 
}
*/
char* func2(int n,char* str)
{
	sprintf(str,"%d",n);
	return str;
}
char* func3(int n)
{
	static char buff[10]={0};
	sprintf(buff,"%d",n);
	return buff;
}
char* func4(int n)
{
	char *buff=(char*)malloc(10*sizeof(char));
	sprintf(buff,"%d",n);
	return buff;
}

size_t类型问题

malloc的参数是size_t类型,他被定义成了unsigned long…如果你传入一个不是同类型的值,maloch会返回垃圾

sizeof的返回值类型也是unsigne long…所以的话..输出时注意i但

printf("%lu",(unsigned long)sizeof(int));

recallloc

他会扩展堆区的长度

如果realloc可以在原来的地址上找到足够的堆区,,那么他就返回原来的指针

也就是说说它不在原来的地址上跟着衔接..

这是可能罢了

.但是我们可以避免万一的清空

#include<stdio.h>
#include<stdlib.h> 
#include<string.h>

int main()
{
	char* str=(char*)malloc(5*sizeof(char));
	char*buff=(char*)realloc(str,10*sizeof(char));
	if(str!=NULL)
		str=buff;
	else
		puts("wrinig"); 
}


如果我故意让伸长的堆区长度变得更加长……但是地址又发生了改变呢?????

#include<stdio.h>
#include<stdlib.h> 
#include<string.h>

int main()
{
	char *S,*buff,*new_ptr; 
	int arr_offset=0;
	
	S=(char*)malloc(5*sizeof(char));//分配内存 
	strcpy(S,"Dqx,");//写入 
	new_ptr=strchr(S,',');//寻址下一个写入的位置 
	buff=(char*)realloc(S,20);//扩展空间,,指针用buff接受 
	arr_offset= new_ptr-S;//计算写入位置到指针头的偏移量 
	if(buff!=NULL)
	{
		S=buff;//把新扩展的指针头传递给S 
		buff=S+arr_offset;//这里的buff也就真的是一个临时的变量 
		strcpy(buff," good");
	}
	puts(S);
	free(S);
	return 0; 
}

用recallc实现动态的内存分配

#include<stdio.h>
#include<stdio.h>
#include<stdlib.h>

char* func(FILE *fp);
int main()
{
	char* out=NULL;
	FILE *fp=fopen("D:/in.txt","a+");
	if(fp!=NULL)
		out=func(fp);
	puts(out);
	fclose(fp);	
    free(out);
	return 0;
}
char *func(FILE *fp)
{
	char *temp=NULL,*ret=NULL;//因为realloc的不确定性所以我们用到临时指针
	int index=0,max_expand=0;
	char c=0;	
	while((c=getc(fp))!=EOF)
	{
		if(index>=max_expand)//这里如果我们已有内存用超了就再分配
		{
			max_expand+=20;
			temp=(char*)realloc(ret,max_expand);//为什么+1?
			if(temp==NULL)
				return 0;
		} 
		ret=temp;
		ret[index]=c;
		if(c=='\n')
		{
			ret[index]='\0';//把最后那个换行符截断
			break;
		}		
		index++;		
	}
	return ret;	//反对堆区的指针
} 

书山写的

#include<stdio.h>
#include<stdlib.h> 
#include<string.h>
char* agetline(FILE *fp);
int main()
{
	FILE* fp=fopen("D:/in.txt","a+");
	char* ptr; 
	if(fp!=NULL)
		ptr=agetline(fp);
	fclose(fp);
	puts(ptr);
	return 0; 
}
char* agetline(FILE *fp)
{
	char *retbuf=NULL;
	size_t nchmax =0;
	register char c=0;
	size_t nchread =0;
	char *newbuf=NULL;
	
	while((c=getc(fp))!=EOF)//逐个读取文件到文件末尾
	{
		if(nchread>=nchmax)//max会不断的+20,有一天会超过它 //最开始2者为0 
		{
			nchmax+=20;
			/*if(nchread>=nchmax)//当你的读取字节大于我的max 
			{
				free(retbuf);
				return NULL;
			}*/
			newbuf=realloc (retbuf,nchmax+1);
			/*if(newbuf==NULL)
			{
				free(retbuf);
				return NULL;	
			}*/
			retbuf=newbuf;
		}
		
		if(c=='\n')
			break;
			
		retbuf[nchread++]=c;//文件里面读取到的东西都进入了这里面来 ,

	} 
	 if(retbuf!=NULL)
	 {
	 	retbuf[nchread]='\0';
	 	//newbuf=realloc(retbuf,nchread+1);
	 	//if(newbuf!=NULL)
	 		//retbuf=newbuf;
	 }
	 return retbuf;
}

calloc与malloc的区别

calloc的解释是

p=malloc(m*n);
memset(p,0,m*n)

于是就这个区别….

calloc的0值有时候不能代表空指针NULL,还是得自己动手

八. 字符和字符串

九. 布尔表达式与变量

十. 预处理器

很复杂

十一. ANSI/ISO标准C

const 限定词

适用范围

const 限定的对象是运行中不能被赋值的对象…于是他就说它并不是一个完全的真正的常量,,,,,

于是它不能用于

数组维度,case行标,,,,,

const指针

从里到外看,会更加明白

const出现在 * 左边,表示被指物是常量;

如果出现在 * 右边,表示指针本身是常量;

如果出现在 * 两边,表示被指物和指针都是常量。

const char * p;

const在左,常量值

char const *p

const在左,常量值

char * const p

const在右,常量指针

#include<stdio.h>

int main()
{
	int x=1;
	const int* p=&x;
	const int** pp=&p;
	printf("%d",**pp); 		
}

这一段代码标准的没问题

到了const int*一级的也好好

到了二级的指针就水深了…规律还很乱

#include<stdio.h>

int main()
{
	int x=1;
	const int xx=2; 
	int *p1=&x;
	int **pp1=&p1;
	int *** ppp1=&pp1;
	
	const int* p2=&x;
	//*p1==x,*p1不可以修改,x却可以修改
	const int **pp2=&p2;

	const int***ppp2=&pp2;
	
	//ppp2=&pp1;
	//*ppp2=pp1;
	//pp2=&p1; 
	*pp2=p1;
	p2=p1;
	//*p2=10;
}

typedef指针与const

只是提一下

typedef char* dqx;
const dqx var;

var最后不是const char *

main函数使用

…..

其他的ansic问题

#pragma one

详情见书籍P123

当头文件被多次包含的时候,他会确保其内容只会被处理一次…

&arr与arr的区别

假设

int arr[10];

书上说

数组的名字代表了可以遍历整个数组的地址

而&arr或者&arr[0]仅仅代表了首元素的地址

#include<stdio.h>
#include<string.h> 
int main()
{
	int arr[3]={1,2,3};
	char str[10]="dqx";
	printf("%08X\n",arr);
	printf("%08X\n",&arr);
	printf("%08X\n",&arr[0]); 
	printf("%d\n",sizeof(arr) / sizeof(arr[0]));
	printf("%d\n",sizeof(&arr) / sizeof(arr[0]));
	printf("%d\n",sizeof(&arr[0]) / sizeof(arr[0]));
	
	printf("%08X\n",str);
	printf("%08X\n",&str);
	printf("%08X\n",&str[0]);

	
	printf("%d\n",strlen(str));	
	puts("");	
	//printf("%d\n",strlen(&str));//Mingw64直接报错 .gcc只是警告
	printf("%d\n",strlen(&str[0]));		
}

数组名字与首地址的

可以看出….那一个地址的是一模一样的

但是函数处理他们的结果确是不一样的

值得注意的是

求数组的长度

是sizeof(arr)/sizeof(arr[0])

不是其他的形式

memmove()与memcpy()

#include<stdio.h>
#include<string.h> 
void* memmove(void* D,void const *S,size_t len );
int main()
{
	char S[10]="dqxisgood";
	char D[10];
	memmove(D,S,10);
	puts(D);
}
void* memmove(void* D,void const *S,size_t len )
{
	register char*d=D;
	register char const* s=S;
    
    //下面都是一个简单的利用指针赋值的操作
	if(d<s)
		while(len-->0)
			*d++=*s++;
	else
	{
		d+=len;
		s+=len;
		while(len-->0)
			*--d=*--s; 
	 } 
	 return D; 
}

现在我都还不知道为什么官方他要那么去写…………………..

#include<stdio.h>

char* memmove(char* D,char *S,size_t len );
int main()
{
	char S[10]="dqxisgood";
	char D[10];
	memmove(D,S,10);
	puts(D);
}
char* memmove(char* D,char *S,size_t len )
{
    char*d=D;
    char*s=S;
	while(len-->0)
		*d++=*s++; 
	return D;
}

十二. 标准输入与输出

EOF

EOF-->Int 类型

从 EOF 宏的定义中可以看出,EOF 宏的值为 -1,貌似他不会发生什么变化

属于 int 类型的数据,在 32 位系统中,可以表示为 0xFFFFFFFF。

有符号的char才是让EOF停下来

无符号的char就不得行,,无符号的char>0,但是我们的EOF为-1

#include<stdio.h>
#include<stdlib.h> 
#include<string.h>

int main()
{
	char a='\xff';
	char b='\377';
	char c=255;
	unsigned char d=255;
	unsigned char e='\377';
	unsigned char f='\xff';
	
	//char x=0;
	//while((x=getchar())!=EOF);
	while(a==EOF)
	{
		printf("OK,I am Fine.");
		if(b==EOF)
			if(c==EOF)
				break;
	};
	puts("");
	while(d==(unsigned char)EOF)
	{
		printf("OK,I am Fine.");
		if(e==(unsigned char)EOF)
			if(f==(unsigned char)EOF)
				break;
	};
	puts("\nGood..."); 
	return 0;
}

书上是这么说的

如果char是有符号的,对于有符号的-1是正数的255,也是‘\xff’,’\377’

所以EOF碰到她们就会结束

如果char是是正数

EOF会变成255(oh??是吗?我的文件并没有,EOF还是-1)

于是你的unsigned char不可能与我的-1相等.于是你的输入就不停止

print

输出字符%

#include<stdio.h>

int main()
{	
	printf("%%"); 
	return 0;
}
	

他会输出

%
--------------------------------
Process exited after 0.02536 seconds with return value 0
请按任意键继续. . .

*域宽度

#include<stdio.h>

int main()
{	
	long int x=21;
	printf("%0*ld",4,x); //0是人为加上去的,不写就是空格,4是宽度
	return 0;
}
	

#include<stdio.h>

int main()
{	
	long int x=21;
	printf("%04ld",x); //0不写就是空格
	return 0;
}
	

上面2个写法的输出都是

0021
--------------------------------
Process exited after 0.009707 seconds with return value 0
请按任意键继续. . .

用千位一个都好输出货币

代码无法执行,,,,.在p135页

涉及一些函数

#include<stdio.h>
#include<locale.h> 
char* commaprint(unsigned long n);
int main()
{	
	unsigned long x=123456789;
	char *str;
	str=commaprint(x);
	puts(str);
	return 0;
}
char* commaprint(unsigned long n)
{
	static int comma='\0';
	static char retbuf[30];
	char *p=&retbuf[sizeof(retbuf)-1];//让p指针指向了最后一位 
	int i=0;
	if(comma=='\0')//这里一定会进入....为什么要这样设置 
	{
		struct lconv *lcp=localeconv();
		if(lcp!=NULL)
		{
			if(lcp->thousands_sep!=NULL&&*lcp->thousands_sep!='\0')
				comma=*lcp->thousands_sep;
			else
				comma=',';
		}
	}
	*p='\0';
	do
	{
		if(i%3==0&&i!=0)
			*--p=comma;
		*--p='\0'+n%10;
		n/=10;
		i++;
	}
	while(n!=0);
	return p;

}

输出浮点数

对于float还是double..最后pritnf输出的都是double的型

这期间存在一个类型的自动转化,,,,float->double

scanf

#include<stdio.h>
#include<stdlib.h>
#define max 10 

int main()
{	
	int x=0;
	int y=0;
	scanf("%d\n",&x);
	scanf("%d\n",&y);
	printf("%d %d",x,y);
	return 0;
}



输出

1
2
3
1 2
--------------------------------
Process exited after 1.896 seconds with return value 0
请按任意键继续. . .

为什么?????

在3之前他才会真正的寻找到空字符!!!!!!!!!!!!!

使用scanf域gets的茬

#include<stdio.h>
#include<stdlib.h>
#define max 10 

int main()
{	
	int n=97;
	char str[80];
	
	puts("输入1");
	scanf("%d",&n);
	puts("输入2"); 
	gets(str);
	printf("%s-->%d",str,n);
	return 0; 
}

gets有个原则

遇到换行符就停止读取…….

例子1

输入12然后换行

输入1
12
输入2
-->12
--------------------------------
Process exited after 4.625 seconds with return value 0
请按任意键继续. . .

你的stdin是12和换行符号

成功读取了12后,就flussh了一下剩下了换行符????????是吗??我保持意见

于是gets读取换行符后直接退出

所以才hi有上面的结果

例子2

输入66空格再dqx

输入1
66 dqx
输入2
 dqx-->66
--------------------------------
Process exited after 4.964 seconds with return value 0
请按任意键继续. . .

你的stdin中成功的去66后flussh一下剩下空格与dqx

然后就读入到str中

针对上面的情况..我们最好都用scanf或者都用gets

但是改进方案

#include<stdio.h>
#include<stdlib.h>
#define max 10 

int main()
{	
	int n=97;
	char str[80];
	
	puts("输入1");
	scanf("%d",&n);
	fflush(stdin); 
	puts("输入2"); 
	gets(str);
	printf("%s-->%d",str,n);
	return 0; 
}

这样就不会存在吸收多于的空格..你的空格会被flush给修改

scanf的缓冲区刷新

如果不按照scanf的%格式输入的话..他会跳出scanf.而在stdin’中的数据并没有消失,当遇到下一个scanfh回被读取

所以的话

对于代码

#include<stdio.h>
#include<stdlib.h>

int main()
{	
	int n=0,flag=0;
	while(1)
	{
		flag=scanf("%d",&n);
		if(flag!=1)
			puts("input again"); 		
		else
			break; 
	} 
	printf("%d\n",n); 
	
	return 0; 
}


如果你输入字符‘X’,他会无限的循环,,,不会停止,,你无法输入

就是因为你的‘X’不是按照它的格式,,,没有被它读取

如果加上fflush,刷新一下stdin..就有刷新当前的输入…(相当于把以前的数据晴空)

#include<stdio.h>
#include<stdlib.h>

int main()
{	
	int n=0,flag=0;
	while(1)
	{
		flag=scanf("%d",&n);
		
		if(flag!=1)
		{
			puts("input again"); 
			fflush(stdin);
		}		
		else
			break; 
	} 
	printf("%d\n",n); 
	
	return 0; 
}

于是你不按照格式输入也不会无限循环

其它的输入函数

sprintf

int sprintf(char *str, const char *format, ...)

关于他是怎么用的……不做详细的阐述..具体的话到了时间再说

出现的问题

sprintf不能确定缓冲区大小./…可能存在内存溢出后改写其它的内存区..我们可以用到

%.NS,%.*S

但是我们还有一个函数

snprrintf

相比于sprintf…这个函数做了限制

还有一种方法…那数据写入文件…之后再拿出..

把数据写入动态内存分配

sprintf()也会有返回值..至于是什么自己查看

gets

char *gets(char*);

他会读取字符串…一直遇到换行或者EOF时结束

于是把你的换行符号丢弃加上空字符‘\0’

如果读入成功,则返回与参数 buffer 相同的指针;

如果读入过程中遇到 EOF 或发生错误,返回 NULL 指针。

因此,在遇到返回值为 NULL 的情况,要用 ferror 或 feof 函数检查是发生错误还是遇到 EOF。

根据运行结果,当用户在键盘上输入的字符个数大于缓冲区 buffer 的最大界限时,gets 函数也不会对其进行任何检查,因此我们可以将恶意代码多出来的数据写入堆栈。可能成为病毒的入口

fgets

char *fgets(D,len,S);

即给定参数 n=len,fgets 函数只能读取 n-1 个字符(包括换行符)。如果有一行超过 n-1 个字符,那么 fgets 函数将返回一个不完整的行(只读取该行的前 n-1 个字符)

缓冲区总是以 null('\0') 字符结尾,对 fgets 函数的下一次调用会继续读取该行。

也就是说,每次调用时,fgets 函数都会把缓冲区的最后一个字符设为 null('\0'),这意味着最后一个字符不能用来存放需要的数据。所以如果某一行含有 size 个字符(包括换行符),要想把这行读入缓冲区,要把参数 n 设为 size+1,即多留一个位置存储 null('\0')。

总而言之就是他会把给定大小的最后一个位置设计为哦‘\0’

如果你非要写那么长,就得分配len+1..

fflush(sdtdin)

书上说fflush时对输出stdout有效的

这点勾起了我对count<<“Ok.I am Fine”<<endl;

确实也是哈…fflush是对stdout的一个实现

那么真的对stdin无效….也不是

有些编译器实现了fflush(stdin)放弃未读取的字符..不过这里面应该还有更加深奥的东西….有一本书叫做<C标准库>…哇我…

文件读写

涉及到指针的函数封装(容易出错)

对指针的理解不是很到位的话

#include<stdio.h>
#include<stdlib.h>
#define max 10 
void func(FILE *fp);
int main()
{	
	FILE *fp1=NULL;
	func(fp1);
	fp1; 
}
void func(FILE *fp2)
{
	fp2=fopen("D:/in.txt","w");
}

这个函数对main的fp1不会有任何的影响……main的fp还是NULL

但是我们非要那么干的话

方法一

书上是这样写的,..但是函数岂不是返回了一个局部变量??????????有冲突

#include<stdio.h>
#include<stdlib.h>

FILE* func(FILE *fp2);
int main()
{	
	FILE *fp1=NULL;
	fp1=func(fp1);
	fp1; 
}
FILE* func(FILE *fp2)
{
	fp2=fopen("D:/in.txt","w");
	return fp2;
}

方法二..引入二级指针..这才是解释你之前那个为什么是错的了…你传入的只是一个副本罢了

#include<stdio.h>
#include<stdlib.h>

void func(char*,FILE **fp2);
int main()
{	
	FILE *fp1=NULL;
	func("D:/in.txt",&fp1);
}
void func(char* filename,FILE **fp1)
{
	
	*fp1=fopen(filename,"w");
}

或者

#include<stdio.h>
#include<stdlib.h>

void func(FILE **fp2);
int main()
{	
	FILE *fp1=NULL;
	func(&fp1);
	printf("%p\n",fp1);
}
void func(FILE **fp1)
{
	*fp1=NULL;
	*fp1=fopen("D:/in.txt","w");
	if(*fp1!=NULL);
		printf("%p\n",*fp1);
}

十三 库函数

从字符串中如何去除子字符串

好比dqx is good中去除字符串is

way1

#include <stdio.h>
#include <string.h>


int main()
{
	char S[40]="dqx is good";
	char D[12];
	
	strncpy(D, S+4, 2);
	D[6]='\0';
	puts(D);
   	return(0);
}

标签:,main,return,int,char,printf,include
From: https://www.cnblogs.com/re4mile/p/17013325.html

相关文章