Question: 关于C标准库实现中的内存管理细节问题

《深入解析Windows操作系统-Windows Internals》 《Windows核心编程》

引用 6 楼 pengzhixi 的回复:可以去看看TLS方面的东西  TLS全称为Thread Local Storage，是Windows为解决一个进程中多个线程同时访问全局变量而提供的机制。TLS可以简单地由操作系统代为完成整个互斥过程，也可以由用户自己编写控制信号量的函数。当进程中的线程访问预先制定的内存空间时，操作系统会调用系统默认的或用户自定义的信号量函数，保证数据的完整性与正确性。

刚好就是你括号里说的那种情况  可以再次调用该函数，可以发现之前的指针处的值已被改变

可以去看看TLS方面的东西

引用 4 楼 hijack00 的回复:你说的意思是会复制一份到线程空间？那么这会涉及到内存管理吗？对这方面了解不是太多，请指教！  就是这个静态变量是每个线程都有一份，互不干扰。

引用 3 楼 akirya 的回复:[quote=引用 2 楼 hijack00 的回复:] [quote=引用 1 楼 qq423399099 的回复:] localtime() return a pointer to static data and hence are not thread-safe  localtime返回的是static data,所以不需要释放 同时在进行多线程编程时是危险的，不是可重入的  谢谢，利用静态存储方式虽然会带来不可重入以及增大文件体积的问题，不过确实也不用考虑动态内存分配了。 [/quote] 现在是用线程局部存储，多线程可重入。[/quote]  你说的意思是会复制一份到线程空间？那么这会涉及到内存管理吗？对这方面了解不是太多，请指教！

引用 2 楼 hijack00 的回复:[quote=引用 1 楼 qq423399099 的回复:] localtime() return a pointer to static data and hence are not thread-safe  localtime返回的是static data,所以不需要释放 同时在进行多线程编程时是危险的，不是可重入的  谢谢，利用静态存储方式虽然会带来不可重入以及增大文件体积的问题，不过确实也不用考虑动态内存分配了。 [/quote] 现在是用线程局部存储，多线程可重入。

引用 1 楼 qq423399099 的回复:localtime() return a pointer to static data and hence are not thread-safe  localtime返回的是static data,所以不需要释放 同时在进行多线程编程时是危险的，不是可重入的  谢谢，利用静态存储方式虽然会带来不可重入以及增大文件体积的问题，不过确实也不用考虑动态内存分配了。


Question: 双链表删除指定节点的问题

调试了半天。发现前驱节点没有初始化。谢谢！

仅供参考：//不带表头结点的双向链表 #include <stdio.h> #include <stdlib.h> #include <malloc.h> #include <time.h> #include <locale.h> struct NODE {     int          data;     struct NODE *next;     struct NODE *prev; } *head,*p,*q,*s,*p1,*p2,*q1,**ta; int i,k,n,t,m,v,N=10; int main() {     setlocale(LC_ALL,"chs");     srand(time(NULL));      head=NULL;      printf("创建%d个节点的双向链表:",N);//创建N个节点的双向链表     p=head;     for (i=0;i<N;i++) {         q=(struct NODE *)malloc(sizeof(struct NODE));         if (NULL==q) exit(1);         q->data=rand()%100;//填写0..99的随机值         q->next=NULL;         q->prev=NULL;         if (NULL==p) {             head=q;             p=head;         } else {             p->next=q;             q->prev=p;             p=q;         }     }      //输出整个双向链表     s=head;     while (1) {         if (NULL==s) {             printf("\n");             break;         }         printf("%02d<->",s->data);         if (NULL!=s->next && s->next->prev!=s) {printf(" !\n");break;}         s=s->next;     }      k=3;     v=5;     printf("将值为%d的结点插入到双向链表的第%d个结点前:",v,k);//将值为v的结点插入到双向链表的第k个结点前     n=0;     p=head;     while (1) {         if (NULL==p) {             break;         }         n++;         if (k==1) {             q=(struct NODE *)malloc(sizeof(struct NODE));             if (NULL==q) exit(1);             q->data=v;             q->next=head;             q->prev=NULL;             head->prev=q;             head=q;             break;         } else {             if (k-1==n) {                 q=(struct NODE *)malloc(sizeof(struct NODE));                 if (NULL==q) exit(1);                 q->data=v;                 q->next=p->next;                 q->prev=p;                 p->next->prev=q;                 p->next=q;                 break;             }         }         p=p->next;     }      //输出整个双向链表     s=head;     while (1) {         if (NULL==s) {             printf("\n");             break;         }         printf("%02d<->",s->data);         if (NULL!=s->next && s->next->prev!=s) {printf(" !\n");break;}         s=s->next;     }      k=5;     printf("删除第%d个节点:",k);//删除第k个节点     n=0;     p=head;     while (1) {         if (NULL==p) {             break;         }         n++;         if (k==1) {             q=head;             head=head->next;             head->prev=NULL;             free(q);             break;         } else {             if (k-1==n) {                 q=p->next;                 if (q) {                     p->next=q->next;                     if (q->next) q->next->prev=p;                     free(q);                 }                 break;             }         }         p=p->next;     }      //输出整个双向链表     s=head;     while (1) {         if (NULL==s) {             printf("\n");             break;         }         printf("%02d<->",s->data);         if (NULL!=s->next && s->next->prev!=s) {printf(" !\n");break;}         s=s->next;     }      printf("从小到大排序:");//从小到大排序     for (p=head,p1=NULL;p!=NULL;p1=p,p=p->next) {         for (q=p->next,q1=p;q!=NULL;q1=q,q=q->next) {             if (p->data > q->data) {                  //交换data //              printf("swap %02d %02d\n",p->data,q->data); //              t=p->data;p->data=q->data;q->data=t;                  //或者                  //交换prev和next //              printf("swap %02d %02d\n",p->data,q->data);                 if (p==head) {//p是头                     if (p->next==q) {//pq挨着                         head=q;                         head->prev=NULL;                         p->next=q->next;                         p->prev=q;                         if (q->next) q->next->prev=p;                         q->next=p;                         q=p;                         p=head;                     } else {//pq不挨着                         head=q;                         head->prev=NULL;                         p2=p->next;                         p->next=q->next;                         p->prev=q->prev;                         if (q->next) q->next->prev=p;                         q->next=p2;                         p2->prev=q;                         q1->next=p;                         q=p;                         p=head;                     }                 } else {//p不是头                     if (p->next==q) {//pq挨着                         p1->next=q;                         p->next=q->next;                         p->prev=q;                         if (q->next) q->next->prev=p;                         q->next=p;                         q->prev=p1;                         q=p;                         p=p1->next;                     } else {//pq不挨着                         p1->next=q;                         p2=p->next;                         p->next=q->next;                         p->prev=q->prev;                         if (q->next) q->next->prev=p;                         q->next=p2;                         p2->prev=q;                         q->prev=p1;                         q1->next=p;                         q=p;                         p=p1->next;                     }                 }                  //输出整个双向链表 //              s=head; //              while (1) { //                  if (NULL==s) { //                      printf("\n"); //                      break; //                  } //                  printf("%02d<->",s->data); //                  if (NULL!=s->next && s->next->prev!=s) {printf(" !\n");break;} //                  s=s->next; //              } //              getchar();             }         }     }      //输出整个双向链表并计算链表长度n     n=0;     s=head;     while (1) {         if (NULL==s) {             printf("\n");             break;         }         printf("%02d<->",s->data);         if (NULL!=s->next && s->next->prev!=s) {printf(" !\n");break;}         n++;         s=s->next;     }      printf("将整个链表逆序:");//将整个链表逆序     if (n>=2) {         p=head;         q=p->next;         p->next=NULL;         while (1) {             q1=q->next;             q->next=p;             p->prev=q;             p=q;             q=q1;             if (NULL==q) break;         }         head=p;         head->prev=NULL;     }      //输出整个双向链表     s=head;     while (1) {         if (NULL==s) {             printf("\n");             break;         }         printf("%02d<->",s->data);         if (NULL!=s->next && s->next->prev!=s) {printf(" !\n");break;}         s=s->next;     }      m=4;     n=6;     printf("将双向链表中前%d个结点和后%d个结点进行互换:",m,n);//将双向链表中前m个结点和后n个结点进行互换,m+n为链表总长     k=0;     p=head;     while (1) {         if (NULL==p) {             break;         }         k++;         if (m==k) {             q=p;         }         s=p;         p=p->next;     }     q1=head;     head=q->next;     head->prev=NULL;     s->next=q1;     q1->prev=s;     q->next=NULL;      //输出整个双向链表     s=head;     while (1) {         if (NULL==s) {             printf("\n");             break;         }         printf("%02d<->",s->data);         if (NULL!=s->next && s->next->prev!=s) {printf(" !\n");break;}         s=s->next;     }      //释放所有节点     p=head;     while (1) {         if (NULL==p) {             break;         }         q=p->next;         free(p);         p=q;     }      return 0; } //创建10个节点的双向链表:93<->03<->60<->05<->57<->71<->02<->41<->26<->85<-> //将值为5的结点插入到双向链表的第3个结点前:93<->03<->05<->60<->05<->57<->71<->02<->41<->26<->85<-> //删除第5个节点:93<->03<->05<->60<->57<->71<->02<->41<->26<->85<-> //从小到大排序:02<->03<->05<->26<->41<->57<->60<->71<->85<->93<-> //将整个链表逆序:93<->85<->71<->60<->57<->41<->26<->05<->03<->02<-> //将双向链表中前4个结点和后6个结点进行互换:57<->41<->26<->05<->03<->02<->93<->85<->71<->60<-> //

#include "stdafx.h" #ifndef _LISTHEADERA_H_ #define _LISTHEADERA_H_  typedef int Elment;  struct list { 	Elment listnum; 	list* prior; 	list* pnext; };  #endif 头文件的定义

看了下发现没啥问题。 要么你把头文件listheadera.h也贴一下 或者自己单步调试，看看是哪句的问题。 单步调试和设断点调试（VS IDE中编译连接通过以后，按F10或F11键单步执行，按Shift+F11退出当前函数；在某行按F9设断点后按F5执行停在该断点处。）是程序员必须掌握的技能之一。

你给一半代码别人没法帮你调试  出错时点中断，断下来通过调用堆栈、变量窗口观察分析原因，必要时可单步跟踪程序运行


Question: 九度oj 1009 题目vs2012运行正确，oj错误

题目呢？？我也想看看oj题

主函数最后是不是要return 0 啊

边界条件 输入输出格式 ……


Question: c语言关于类型转换的问题

char不一定是代表signed char      signed char - type for signed character representation.      unsigned char - type for unsigned character representation. Also used to inspect object representations (raw memory).      char - type for character representation which can be most efficiently processed on the target system (has the same representation and alignment as either signed char or unsigned char, but is always a distinct type). Multibyte characters strings use this type to represent code units. The character types are large enough to represent 256 different values (in order to be suitable for storing UTF-8 encoded data) (since C++14)    http://en.cppreference.com/w/cpp/language/types

引用 2 楼 永夜将尽的回复:[quote=引用 1 楼 xyless 的回复:] unsigned char的范围就是0-255啊  unsigned char GetNsid(char nsid)  不，你看这个函数的参数，是char类型，返回值才是uchar，传参数的时候不应该像func那样进行默认的类型转换么？[/quote] 你打印的就是函数返回值，将unsigned char扩展为int输出

而 unsigned char 范围是0～255， 不是类型转换能改变的。 类型转换得到的是转换后的数据（包括类型） 有些时候，仅仅是解释方式的改变 例如把 char 转换为 unsigned char ，实际上并不需要做什么

unsigned char  和 char ---------可能实现为 signed char ，也可能实现为unsigned char------- signed char  即便互相转换，但从位上看，每一位都没改变（0还是0,1还是1），跟原来一模一样。 改变的仅仅是 类型的名字。仅仅改变了类型。

引用 1 楼 xyless 的回复:unsigned char的范围就是0-255啊  unsigned char GetNsid(char nsid)  不，你看这个函数的参数，是char类型，返回值才是uchar，传参数的时候不应该像func那样进行默认的类型转换么？

unsigned char的范围就是0-255啊


Question: 释放动态内存小问题,看的视频为什么倒数第二行要写q = null,不是已经释放内存了吗

防止意外使用被释放的指针，因为判断指针释放释放，一般是判断      if(NULL==p){}

楼上都说的这么底层。。。。。感觉对楼主还说还早了点啊。

防止后面不小心又使用了已经释放了的内存，这是一种习惯，当然也可以不写

但是安全性来看，可大不一样了。、  (1)在没有 "p = NULL;"时，指针p是指向一个内存地址的，只是这个内存地址对当前程序是非法的。对黑客来说，这可是一个机会。 每次程序崩溃时几乎都会在同一个地址（p所指的位置），这个地址会很容易到黑客手上的，然后黑客可以在指定的内存地址上事先布置好自己的代码，然后运行程序，崩溃时就会到黑客指定的代码上去，结果不堪设想啦。  (2)在有"p = NULL;"时，程序同样崩溃，但崩溃的地址是0. 0地址对任何程序都是非法的，所以再牛的黑客也不能把自己的代码布置到内存地址是0的空间上。所以对这种情况，黑客也只能面对崩溃 翻白眼，感叹生不逢时啊。  http://blog.csdn.net/zhangyulin54321/article/details/7869127

q = null; 目的是防止二次释放内存的一个习惯写法。如果q只在这个函数里面用，不置为null也是没有问题的。 因为如果q不等于null如果free的时候回再次释放q指向的地址空间。这个时候就会有问题。 如果q等于null了，就不会有问题了。  如果涉及多个函数间传递指针等复杂的操作，最好是置为null否则很容易出问题。 这就是一个好习惯。防止出错。

如果被意外使用已经释放的指针,null指针直接就崩溃，方便查找原因

野指针，防止程序崩溃

因为代码是常会修改的。比如说现在这是代码的最后一行，以后再加点功能就不是了，这时设的null就有用了。不怕浪费就怕bug.

我就可以将自己的代码布置到内存地址是0的空间上。  其实电脑开机后物理内存的每个字节都是可读写的，从来不会因为所谓的new、delete或malloc、free而被创建、销毁。区别仅在于操作系统内存管理模块在你读写时是否能发现并是否采取相应动作而已。操作系统管理内存的粒度不是字节而是页，一页通常为4KB。

已经释放的内存理论上是无效的，但对他的任何使用也可能带来许多的问题，会发生许多的意外，最好还是p=NULL吧，这样他的状态就明确了。


Question: “计算两个日期差值”代码优化提升

说一些代码上的问题： 1、date2num函数名字看不出是在干什么？“日期转数字”是什么意思？改为date_distance()或是days_of_two_date()之类的，更明确 2、date2num(DATE Date)，应该用const DATE* 修饰Data，注意函数的实参拷贝和指针参数对性能的影响 3、int a[2][13]，为什么是13列？为什么第一列的数字都是0？ 4、代码没有合理的空格，看着很拥挤 5、一行最好只定义一个变量，而且定义的时候就初始化

仅供参考：#include <afxdisp.h> #include <stdio.h> #include <stdlib.h> int main(int argc, TCHAR* argv[]) {     COleDateTime t;     COleDateTimeSpan ts;     CString s,fmt;     int nYear;     int nMonth;     int nDay;     int nHour;     int nMin;     int nSec;     int lDays;     int nHours;     int nMins;     int nSecs;     int i,N;     // initialize MFC and print and error on failure     if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) {         printf("Fatal Error: MFC initialization failed\n");         return 1;     }     if (argc<13) {         printf("Usage:%s sYYYY sMM sDD shh smm sss pDD phh pmm pss n {SQL|YYYY|YY}\n",argv[0]);         return 2;     }     if (stricmp(argv[12],"SQL")==0) fmt="%Y-%m-%d %H:%M:%S";     else if (stricmp(argv[12],"YYYY")==0) fmt="%Y%m%d %H%M%S";     else if (stricmp(argv[12],"YY")==0) fmt="%y%m%d %H%M%S";     else {         printf("Usage:%s sYYYY sMM sDD shh smm sss pDD phh pmm pss n {SQL|YYYY|YY}\n",argv[0]);         return 3;     }     nYear =atoi(argv[ 1]);     nMonth=atoi(argv[ 2]);     nDay  =atoi(argv[ 3]);     nHour =atoi(argv[ 4]);     nMin  =atoi(argv[ 5]);     nSec  =atoi(argv[ 6]);     lDays =atoi(argv[ 7]);     nHours=atoi(argv[ 8]);     nMins =atoi(argv[ 9]);     nSecs =atoi(argv[10]);     N     =atoi(argv[11]);     if (N<=0) {         printf("Usage:%s sYYYY sMM sDD shh smm sss pDD phh pmm pss n {SQL|YYYY|YY}\n",argv[0]);         return 4;     }     t=COleDateTime( nYear, nMonth, nDay, nHour, nMin, nSec);     ts=COleDateTimeSpan( lDays, nHours, nMins, nSecs );     for (i=1;i<=N;i++) {         s=t.Format(fmt);         printf("%08d %s\n",i,s);         t=t+ts;     }     return 0; }

The COleDateTime class handles dates from 1 January 100 – 31 December 9999.  COleDateTimeSpan::GetDays long GetDays( ) const;  Return Value  The day portion of this date/time-span value.  Remarks  Call this member function to retrieve the day portion of this date/time-span value.  The return values from this function range between approximately  – 3,615,000 and 3,615,000.  For other functions that query the value of a COleDateTimeSpan object, see the following member functions:   GetHours   GetMinutes   GetSeconds   GetTotalDays   GetTotalHours   GetTotalMinutes   GetTotalSeconds  COleDateTimeSpan Overview |  Class Members |  Hierarchy Chart  See Also   COleDateTimeSpan::SetDateTimeSpan    COleDateTime::operator +, - COleDateTime operator +( const COleDateTimeSpan& dateSpan ) const;  COleDateTime operator -( const COleDateTimeSpan& dateSpan ) const;  COleDateTimeSpan operator -( const COleDateTime& date ) const;  Remarks  COleDateTime objects represent absolute times. COleDateTimeSpan objects represent relative times. The first two operators allow you to add and subtract a COleDateTimeSpan value from a COleDateTime value. The third operator allows you to subtract one COleDateTime value from another to yield a COleDateTimeSpan value.  If either of the operands is null, the status of the resulting COleDateTime value is null.  If the resulting COleDateTime value falls outside the bounds of acceptable values, the status of that COleDateTime value is invalid.  If either of the operands is invalid and the other is not null, the status of the resulting COleDateTime value is invalid.  For more information on the valid, invalid, and null status values, see the m_status member variable.  For more information about the bounds for COleDateTime values, see the articleDate and Time: Automation Support in Visual C++ Programmer’s Guide.  COleDateTime Overview |  Class Members |  Hierarchy Chart  See Also   COleDateTime::operator +=, -=, COleDateTime::GetStatus, COleDateTimeSpan

date.txt中存放样例如下： 2016 12 31 2016 08 07

#include<stdio.h>	// sscanf( )、fopen( )、fgets( ) #include<stdlib.h>	// exit( )  //日期结构体 typedef struct { 	char year[5]; 	char month[3]; 	char day[3]; }DATE;  //判断是否是闰年 int IsleapYear(int y) { 	if((y%400==0) || (y%100!=0) && (y%4==0)) 		return 1; 	else 		return 0; }  //将日期转化成数字（即从指定的一天算起到所求日期的天数） long date2num(DATE Date) { 	int Iyear,Imonth,Iday; 	long dateNum; 	int i,h=0,count=0; 	int a[2][13]={{0,31,28,31,30,31,30,31,31,30,31,30,31},{0,31,29,31,30,31,30,31,31,30,31,30,31}}; 	 	Iyear=atoi(Date.year); 	Imonth=atoi(Date.month); 	Iday=atoi(Date.day); 	 	//闰年增加的天数 	for(i=0;i<Iyear;i++) 		if (IsleapYear(i)) 			count=count+1; 	 	//最后一年中各个月份的天数 	for(i=0;i<Imonth;i++) 		h=h+a[IsleapYear(Iyear)][i]; 	 	dateNum=(Iyear-1)*365+count+h+Iday; 	return dateNum; }  int main( ) { 	FILE	*Fp; 	char	Str[12]; 	DATE	DateStart, DateEnd; 	long	NumStart,NumEnd; 	int		Interval; 	if((Fp=fopen("date.txt", "r+"))==0) 	{ 		printf("打开date.txt文件失败\n"); 		exit(1); 	} 	 	fgets(Str, sizeof(Str), Fp); 	if((sscanf(Str, "%s%s%s", DateStart.year, DateStart.month, DateStart.day))!=3) 	{ 		printf("信息丢失\n"); 		exit(1); 	} 	 	fgets(Str, sizeof(Str), Fp); 	if((sscanf(Str, "%s%s%s", DateEnd.year, DateEnd.month, DateEnd.day))!=3) 	{ 		printf("信息丢失\n"); 		exit(1); 	} 	 	/* printf("计算%s年%s月%s日起到%s年%s月%s日止间隔的天数\n", DateStart.year, DateStart.month, DateStart.day, DateEnd.year, DateEnd.month, DateEnd.day); 	printf("间隔%d天\n", Interval); */ 	 	NumStart=date2num(DateStart); 	NumEnd=date2num(DateEnd); 	Interval=NumEnd-NumStart; 	fprintf(Fp,"\n%s年%s月%s日起到%s年%s月%s日止间隔的天数%d天。", DateStart.year, DateStart.month, DateStart.day, DateEnd.year, DateEnd.month, DateEnd.day, Interval);  	 	fclose(Fp); }


Question: C语言标准库函数原型设计

其实当初，FFFF也就是EOF(-1) 本来就是文件结束标志。 文本文件写入FFFF，表示文件结束。 读文件的时候，读到FFFF表示文件结束了。 所以 getc,getchar 返回 -1 表示文件结束 而 控制台 用^Z或者^D ，表示文件输入结束，只是一个方便的法子

引用 17 楼 paschen 的回复:[quote=引用 13 楼 hijack00的回复:][quote=引用 11 楼 paschen 的回复:] 历史原因，putchar  等一系列函数参数类型都是int  部分认同是历史原因，但还是坚持认为可能有技术层面的考虑。毕竟库的设计者都是大牛，不可能是随随便便就决定了的[/quote] 读下这个：  http://wenku.baidu.com/link?url=1jdn5PU6EurW8Sqpx3VubBJZsTC2OLUWeHCbWq6VEfRahnlYb1XcSKhta-cBnlv9uvAGjPyzbWB38wVxNfcYyw8gtFqGD4cYHNWMwC0sOTG[/quote]  浏览了一下这篇文章，但是其权威性还有待证实。 文中提到 (c = fgetc(fp)) != EOF /* 读到值为FF的字符，误认为 EOF */  按我的理解，fgetc应该是读不到值为FF的字符的。fgetc要么返回一个ASCII字符（这里不考虑读取中文字符的情况），要么是EOF，这样EOF就表示文件末尾或者输入结束。 要想读入类似FF这样的值，我认为应该要用二进制模式打开文件，并且用fread这样的函数来读取文件内容，这样才能将文件内容按照字节进行读取。 而fscanf,fpritnf,fgetc,fputc这一类的函数应该是用于读写文本文件的。

其实，数学运算，大部分，统一为 int,double  是为了编译器，实现的方便 这样，库代码的实现，也简单方便。 同样，参数传递也是一样 为了编译器实现的方便。

引用 13 楼 hijack00的回复:[quote=引用 11 楼 paschen 的回复:] 历史原因，putchar  等一系列函数参数类型都是int  部分认同是历史原因，但还是坚持认为可能有技术层面的考虑。毕竟库的设计者都是大牛，不可能是随随便便就决定了的[/quote] 读下这个：  http://wenku.baidu.com/link?url=1jdn5PU6EurW8Sqpx3VubBJZsTC2OLUWeHCbWq6VEfRahnlYb1XcSKhta-cBnlv9uvAGjPyzbWB38wVxNfcYyw8gtFqGD4cYHNWMwC0sOTG

double 类型早就有了，因为fortran 就有 real (对应 float),double 两种不同精度的浮点数了， fortran 是最早的高级语言了  因为当时 double 精度够用了 float 精度不太够用 所以，doule 就作为 浮点类型的代表了 所以标注库的数学函数，都是 double 类型的  至于浮点数协处理器，那是根据 以前浮点数的使用情况，制造出来的 不是因为有了浮点处理器，才有float,double 的 也许 long double 跟浮点处理器有点关系。 float ,double 是早就存在的  浮点数，曾经有个，48Bits 浮点数,这是 pascal最初，常用浮点类型。

有瑕疵也不必回避, 更不用为他披上神圣的光环. 站在现在的观点看过去很多有不完善的地方, 并不代表你回到过去就能做得更好.

特意下了c程序设计语言 可以去看看A.7.3.2 Function Calls 这一节内容。里面讲了这些

引用 11 楼 paschen 的回复:历史原因，putchar  等一系列函数参数类型都是int  部分认同是历史原因，但还是坚持认为可能有技术层面的考虑。毕竟库的设计者都是大牛，不可能是随随便便就决定了的

引用 3 楼 pengzhixi 的回复:你看下EOF的值是-1，你觉得char不能表示么？这方面我觉得更是为了统一。内存对齐这个有点扯了。你传参是从char传过去的，那这个char在哪？不还是在内存么？那么你觉得这个char会对齐么？所以就只是一个算术转换做类型提升而已。而且对齐这个在不同体系结构下标准都不一样。没办法统一的。  EOF 的 -1 和 char 的 -1 是不同的.  如果用 char 的 -1  0xFF 来表示 EOF, 当你读取一个二进制文件时, 难道遇到文件里面的 0XFF 就代表文件结束了? 要表示 EOF 必须用 char 的值范围外的值才行.

历史原因，putchar  等一系列函数参数类型都是int

引用 9 楼 pengzhixi 的回复:[quote=引用 8 楼 user-5 的回复:] [quote=引用 5 楼 pengzhixi 的回复:] 那float提升为double呢？你总不能也说是内存对齐吧。  应该是为了精度吧。  而且，计算及的浮点运算器通常只能处理某一种特定类型的浮点数，一般是 double 或者一种更长的浮点类型。所以在计算的时候，基本还是要转换的。[/quote]现在是可以说为了提高精度和表示范围。但是k&r那个时候应该不是。那个时候是否有专门的浮点运算单元都不一定。所以那个时候应该还说不上为了提高精度。[/quote]  K&R 的时代，C 是没有标准的。K&R 的 The C programming language 是 1978 年出版的，实际是当时 K&R C 的事实标准。  然后刚刚去翻了一下，浮点单元 8087 出现是在 1980 年，IEEE-754 成文是在 1985 年（8087是在IEEE 754 草案阶段实现的），C 的第一版标准是在 1989 年。  在 C&R C 2nd Edition (1988年) 里找到这样一段话： Notice that floats in an expression are not automatically converted to double; this is a change fro the original definition. In general, mathematical functions like those in <math.h> will use double precision. The main reason for using float is to save storage in large arrays, or less often, to save time on machines where double-precision arithmetic is particularly expensive. 所以，当时跟现在还是有很多不一样的。

引用 8 楼 user-5 的回复:[quote=引用 5 楼 pengzhixi 的回复:] 那float提升为double呢？你总不能也说是内存对齐吧。  应该是为了精度吧。  而且，计算及的浮点运算器通常只能处理某一种特定类型的浮点数，一般是 double 或者一种更长的浮点类型。所以在计算的时候，基本还是要转换的。[/quote]现在是可以说为了提高精度和表示范围。但是k&r那个时候应该不是。那个时候是否有专门的浮点运算单元都不一定。所以那个时候应该还说不上为了提高精度。

引用 5 楼 pengzhixi 的回复:那float提升为double呢？你总不能也说是内存对齐吧。  应该是为了精度吧。  而且，计算及的浮点运算器通常只能处理某一种特定类型的浮点数，一般是 double 或者一种更长的浮点类型。所以在计算的时候，基本还是要转换的。

额 类型提升带来的好处就像你说的 可能在某些体系结构上是处于对齐的。访问和操作都会十分高效。至于通过寄存器传参的就不管了。效率怎么都不会太差。

引用 5 楼 pengzhixi 的回复:那float提升为double呢？你总不能也说是内存对齐吧。  我只是以一种不太严谨的说法表达了与你类似的意思。我已经说了我那个说法不严谨了。而且对于ARM直接将参数复制到寄存器的做法，甚至都不涉及到内存。此外，你说的float提升为double类型，或者更复杂的情况（例如实参是结构体等等复合类型）我也考虑到了，所以我上面说的是“大部分都是4字节对齐”，这包括（char,short,int,指针）等等情况。如果真的要钻牛角尖的话，在64位系统上，有可能大部分情况下就是8字节对齐了（我对64位系统了解不多，如果说错了，当我没说）

那float提升为double呢？你总不能也说是内存对齐吧。

引用 3 楼 pengzhixi 的回复:你看下EOF的值是-1，你觉得char不能表示么？这方面我觉得更是为了统一。内存对齐这个有点扯了。你传参是从char传过去的，那这个char在哪？不还是在内存么？那么你觉得这个char会对齐么？所以就只是一个算术转换做类型提升而已。而且对齐这个在不同体系结构下标准都不一样。没办法统一的。  1.（早期）正常的char的类型只有0-127是代表ASCII字符的，最高位固定为0。我是从这个角度去说的，从C语言规范来说，char类型自然是可以表示-1。 2. 我关于内存对齐的说法并不严谨。C语言参数传递在不同体系架构下应该是有区别的，例如，在PC平台下看C语言函数的汇编代码，参数好像都是直接入栈的，而且大部分都是4字节对齐（esp+4,esp+8）。而以前看ARM的时候，好像是说参数比较少的时候直接复制到通用寄存器（两年多没碰了，不知道记得准不准确） 3. 我上面说的内存对齐和你的类型提升其实表达的是差不多的意思。我想表达的意思是：当参数入栈的时候，char类型的实参只有一个字节，通过将其转换为一个int类型再入栈，从而也是4字节，这样就可以对齐了。

引用 1 楼 pengzhixi 的回复:好吧，这个是一个历史原因造成的。因为在k&R那个时期，也就是远古时代。函数的声明是这样的。 char* strchr();  然后定义函数是这样 char*strchr(s,c)  char*s; char c; {  ...  } 你可以看到函数声明是没有参数之类的，不像现在的函数原型 需要有参数类型等信息。这样在传递参数的时候会有个算术转换（现在叫类型提升之类的）就是说凡是比int小的类型 比如char,short传进去之后全部提升为int类型。然后你看到定义里面有char c 这样 又从int转型为char 在函数体里面使用。所以估计后面干脆把类型声明为int也是提醒别人 这里会有个算术转换吧。  标准库中有些函数原型设计是设计者别有用心的，例如getchar返回int而不是char，是为了考虑EOF的情况；再如strcpy等函数返回一个指针是为了实现链式调用，等等。所以我比较好奇除了历史原因，库作者有没有技术上的考虑  另外，你的提醒调用者注意类型转换的说法我也表示同意，即使是char类型，在调用函数时，参数入栈的过程中char类型的实参应该也是存入一个32位寄存器（32位机器上）的，所以声明为int类型可能是出于内存对齐以及容错上的考虑。

引用 25 楼 oN5GrzoN 的回复:[quote=引用 1 楼 pengzhixi 的回复:] 好吧，这个是一个历史原因造成的。因为在k&R那个时期，也就是远古时代。函数的声明是这样的。 char* strchr();  然后定义函数是这样 char*strchr(s,c)  char*s; char c; {  ...  } 你可以看到函数声明是没有参数之类的，不像现在的函数原型 需要有参数类型等信息。这样在传递参数的时候会有个算术转换（现在叫类型提升之类的）就是说凡是比int小的类型 比如char,short传进去之后全部提升为int类型。然后你看到定义里面有char c 这样 又从int转型为char 在函数体里面使用。所以估计后面干脆把类型声明为int也是提醒别人 这里会有个算术转换吧。 翻译的不错呀 http://stackoverflow.com/questions/2394011/why-does-strchr-take-an-int-for-the-char-to-be-found[/quote]是的 照搬上面的。


Question: c语言随机数组的问题

1）这个随机函数是一个伪随机，并不是真正意义上的随机函数 2）只要随机种子一致，得到的随机序列就是一致的，如果想随机得到不同的额序列就要设置不同的随机种子，一般使用当前时间作为随机种子   另外，我怎么发现这个赵四，总是进来就复制一段话，题目好像都不看的

需要先用srand初始化种子，否则每次得到的将会是一样的

测试测试测试

不要迷信书、考题、老师、回帖； 要迷信CPU、编译器、调试器、运行结果。 并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。 任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实！

#include <stdio.h> #include <stdlib.h> #include <time.h> #include <string.h> #define N 10  int a[N];  void gen_random() {   int i;   for (i = 0; i < N; i++)   {     a[i] = rand() % 10;   } }  void print_array() {   int i;   for (i = 0; i < N; i++)   {     printf("%d ", a[i]);   }   printf("\n"); }  int main(int argc, char *argv[]) {   srand(time(NULL));   memset(a, 0, N);   print_array();   gen_random();   print_array();    return 0; }

你要先设置种子才行（srand）  #include <stdio.h> #include <string.h> #include <stdlib.h> #include <time.h> #define N 10  int a[N];  void gen_random() { 	int i; 	for (i = 0; i < N; i++) 	{ 		a[i] = rand() % 10; 	} }  void print_array() { 	int i; 	for (i = 0; i < N; i++) 	{ 		printf("%d ", a[i]); 	} 	printf("\n"); }  int main(int argc, char *argv[]) { 	srand(time(NULL)); 	memset(a, 0, N); 	print_array(); 	gen_random(); 	print_array();  	return 0; }


Question: c语言linker error问题

楼主会“编辑”，“查找”吗？

change函数中：    If(TRAIN[i].seat_num<=500);   改成    if(TRAIN[i].seat_num<=500);

177行：If应该是if PS:你的start函数是啥，看不到声明和定义

代码要怎么改

If(TRAIN[i].seat_num<=500); 改为    if(TRAIN[i].seat_num<=500);  关于自己是否适合编程的很简单的测试： 在报纸或杂志上随便找一段约1000字的文章，在Word中输入一遍。输完后再参考下面答案：  A里面有10处以上文字或标点错误 B里面没有文字或标点错误并敢为此跟人打赌 C里面没有文字或标点错误并且字体和排版完全与原稿一致 D打印在半透明的纸上和原稿重叠在一起检查一模一样，且自我感觉很有成就感  A不适合编程（理由：打字准确度偏低、粗心大意） B初级程序员（理由：打字准确度很高、认真细致、自信、理解全角半角概念） C高级程序员（理由：在B的基础上理解字体和排版也是电脑打印的重要因素、但相比D还不够偏执、精益求精、结果可验证） D软件项目经理（理由：能针对项目给出令人信服的细致到极点的需求说明和典型测试用例。用户几乎挑不出毛病。专业！）  如果想从A变成B的话，到我的资源http://download.csdn.net/detail/zhao4zhong1/4084259里面下载“适合程序员的键盘练习”

提示不是很明显了么 文件名 然后哪个符号没定义。


Question: C的小问题，问题都在代码末尾注释

scanf("%d ",&val);    %d后面多了一个空格

for (i=0; i < len; i++)     {         printf("请输入第%d个节点的值:", i+1);         scanf("%d",&val);         PNODE pNew = (PNODE)malloc(sizeof(NODE));            //定义一个NODE型数据变量，并分配动态内存  去掉引号中的空格scanf("%d", &val)；

引用 4 楼 Tion一的回复:[quote=引用 3 楼 paschen 的回复:] scanf()中空白字符(包括/n,space)会使scanf()函数在读操作中略去输入中的零个或者一个或者多个空白字符，空白符可以是space,tab,换行 等等，直到第一个非空白符出现为止。不是很明白试了一下确实可以。 我输入后scanf读到空格是返回输入，下一次scanf是从第一个非空白读取，感觉没影响啊[/quote] 就是还会继续匹配空白符，所以后面不要加空格  \n  之类

引用 3 楼 paschen 的回复:scanf()中空白字符(包括/n,space)会使scanf()函数在读操作中略去输入中的零个或者一个或者多个空白字符，空白符可以是space,tab,换行 等等，直到第一个非空白符出现为止。不是很明白试了一下确实可以。 我输入后scanf读到空格是返回输入，下一次scanf是从第一个非空白读取，感觉没影响啊


Question: 关于格式转换问题

浮点数格式 http://bbs.csdn.net/topics/390676437

printf("b + d = %f\n", (float) b + d); 缺个强制类型转换，%f 是用来输出浮点数的。b+d 是 int 。

printf("b + d = %f\n", b + d);  改成  printf("b + d = %d\n", b + d);

"b+d"打印的结果和"a+c"结果相等(近似相等)，而不是0.00000，难道是因为调用"a+c"运算结果的地址么

%f 是用来输出浮点数的。 b+d 是 int 。  两者不一致是一个未定义行为。


Question: epoll编程，困扰了我6年的问题！求讨论。

引用 35 楼 赵4老师 的回复:[quote=引用 34 楼 这个人很懒，连昵称也没有 的回复:] [quote=引用 6 楼 赵4老师 的回复:] 计算机组成原理→DOS命令→汇编语言→C语言（不包括C++）、代码书写规范→数据结构、编译原理、操作系统→计算机网络、数据库原理、正则表达式→其它语言（包括C++）、架构…… 你这样的回复有屁用。[/quote] 你这样的挖坟连屁用都不顶。[/quote]  既然要回答问题，就要给点实际的。这样的回答，讲真，只能算是灌水。

引用 6 楼 赵4老师 的回复:计算机组成原理→DOS命令→汇编语言→C语言（不包括C++）、代码书写规范→数据结构、编译原理、操作系统→计算机网络、数据库原理、正则表达式→其它语言（包括C++）、架构…… 你这样的回复有屁用。

引用 34 楼 这个人很懒，连昵称也没有 的回复:[quote=引用 6 楼 赵4老师 的回复:] 计算机组成原理→DOS命令→汇编语言→C语言（不包括C++）、代码书写规范→数据结构、编译原理、操作系统→计算机网络、数据库原理、正则表达式→其它语言（包括C++）、架构…… 你这样的回复有屁用。[/quote] 你这样的挖坟连屁用都不顶。

楼主，请参考这位兄弟的经验。https://blog.csdn.net/linuxheik/article/details/73294658

楼主你这个问题我昨天也遇到了，今天总算解了 一开始以为是EPOLLIN的时候没有处理完，后来想了很久才意识到accept的时候也要用循环读到EAGAIN为止 这个在网上大多数的epoll框架都没有这样写的

引用 22 楼 leinchu 的回复:@yangyunzhao @user-9 @pengzhixi  我这里有一个测试socket服务稳定性的程序： http://share.weiyun.com/4dab6e653ec6ec94416e71013ef3c02f  centos 64位下编译的， 你们看看你们的socket服务能不能经得起这个程序24小时的考验。  用法 ./client 线程数 端口 [服务器ip、默认127.0.0.1] 已经过期，麻烦再贴一份

问题没有解决，这个问题太难了，先结贴，再开帖子吧。

引用 27 楼 bluesen 的回复:[quote=引用 3 楼 caojinrong 的回复:] 用了epoll再用线程，好奇葩的做法！  et模式下，socket必须设置为异步，确保每次都将缓冲区的数据读尽，否则是不会进入下一次触发的，估计你的问题就出这了。  说的好！[/quote]  设置为非阻塞的了的，进程还是线程不会成为影响问题的关键吧？ 而且，我这个socket服务并非一问一答的方式，服务器和客户端都有可能主动下发数据

引用 26 楼 yangyunzhao 的回复:[quote=引用 22 楼 leinchu 的回复:] @yangyunzhao @user-9 @pengzhixi  我这里有一个测试socket服务稳定性的程序： http://share.weiyun.com/4dab6e653ec6ec94416e71013ef3c02f  centos 64位下编译的， 你们看看你们的socket服务能不能经得起这个程序24小时的考验。  用法 ./client 线程数 端口 [服务器ip、默认127.0.0.1] 提供一下makefile文件吧[/quote]   很简单，没makefile gcc -g -o client client.c -lpthread

引用 3 楼 caojinrong 的回复:用了epoll再用线程，好奇葩的做法！  et模式下，socket必须设置为异步，确保每次都将缓冲区的数据读尽，否则是不会进入下一次触发的，估计你的问题就出这了。  说的好！

引用 22 楼 leinchu 的回复:@yangyunzhao @user-9 @pengzhixi  我这里有一个测试socket服务稳定性的程序： http://share.weiyun.com/4dab6e653ec6ec94416e71013ef3c02f  centos 64位下编译的， 你们看看你们的socket服务能不能经得起这个程序24小时的考验。  用法 ./client 线程数 端口 [服务器ip、默认127.0.0.1]我直接用的开源的nginx所以怎么压都没问题。

里面有验证稳定性的源码

引用 22 楼 leinchu 的回复:@yangyunzhao @user-9 @pengzhixi  我这里有一个测试socket服务稳定性的程序： http://share.weiyun.com/4dab6e653ec6ec94416e71013ef3c02f  centos 64位下编译的， 你们看看你们的socket服务能不能经得起这个程序24小时的考验。  用法 ./client 线程数 端口 [服务器ip、默认127.0.0.1] 提供一下makefile文件吧

引用 24 楼 pengzhixi 的回复:[quote=引用 22 楼 leinchu 的回复:] @yangyunzhao @user-9 @pengzhixi  我这里有一个测试socket服务稳定性的程序： http://share.weiyun.com/4dab6e653ec6ec94416e71013ef3c02f  centos 64位下编译的， 你们看看你们的socket服务能不能经得起这个程序24小时的考验。  用法 ./client 线程数 端口 [服务器ip、默认127.0.0.1]我直接用的开源的nginx所以怎么压都没问题。[/quote]  我服了

https://github.com/chenshuo/muduo 这个项目描述 epoll 很规范，自己照着写就好。  https://github.com/chenshuo/muduo/blob/master/muduo/net/Channel.cc

学习一下，一直都用的libevent，没有仔细研究过epoll

epoll_ctl(epoll_fd, EPOLL_CTL_DEL, tmpChain->fd, tmpChain->evt) 是有  EPOLL_CTL_ADD 才有 EPOLL_CTL_DEL。就和new 和delete要对应

假死？你没有关闭监听的fd就应该没什么问题吧

我把关闭fd的代码改到主线程了，用了一个链表。然后这种不消失的fd减少了，一晚上有1000多个。  后来又加上了epoll_ctl(epoll_fd, EPOLL_CTL_DEL, tmpChain->fd, tmpChain->evt)  移除fd，这种不减少的fd就变得很少，几个小时只有几十个。但socket监听已经假死了：telnet过去，不断开也不能收发消息！    求大神赶紧出现！ 贴上一段代码（管理socket fd的）： addData 添加到链表，checkData检测断开的释放资源。    typedef struct f_client_data{ 	int server_sock;     int client_sock; 	int fd; 	char ip[20]; 	int port; 	double lastActiveTs; 	int idx0; 	int idx1; 	int createTs; 	struct epoll_event* evt; #ifdef CLOSE_IN_MAIN 	struct f_client_data *next; 	int server_fd; 	int needToBeClosed; #endif } final_client_data;  #ifdef CLOSE_IN_MAIN void addData(struct f_client_data* data){ 	if(tail!=NULL) 		tail->next = data; 	tail = data; 	data->next = NULL; 	data->server_fd = 0; 	data->needToBeClosed = 0; }  void checkData(){ 	tmpChain1 = NULL;//上一个非需要移除的 	tmpChain2 = NULL;//临时保存变量用于free 	tmpChain = head;//保存当前检查的位置 	head = NULL; 	while(1){ 		if(tmpChain!=NULL && tmpChain->next!=NULL){ 			//final_client_data* sock_data = head->cur; 			if(tmpChain->needToBeClosed){ #ifndef EXETIME_DETECT 				printf("[evt = %p\n", tmpChain->evt); #endif 				if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, tmpChain->fd, tmpChain->evt) < 0) 				{ 					//perror("epoll ctl del error."); 					printf("EPOLL_CTL_DEL error at: %d, fd=%d, error: %s\n",__LINE__, tmpChain->fd, errno>0?strerror(errno):"EPOLL_CTL_DEL failed"); 					delEpollCountError ++; 				} 				tmpChain->evt->data.ptr = 0; 				 				closeSocket(tmpChain->fd); #ifndef EXETIME_DETECT 				printf("C,client,%d\n",tmpChain->fd); #endif 				if(tmpChain->server_fd>0){ 					closeSocket(tmpChain->server_fd); #ifndef EXETIME_DETECT 					printf("C,server,%d\n",tmpChain->server_fd); #endif 				}  				if(tmpChain1 != NULL) 					tmpChain1->next = tmpChain->next;  				tmpChain2 = tmpChain; 				tmpChain = tmpChain->next; 				free(tmpChain2); #ifndef EXETIME_DETECT 				printf("]\n"); #endif 			}else{ 				if(head==NULL){ 					head = tmpChain; 				} 				tmpChain1 = tmpChain; 				tmpChain = tmpChain->next; 			} 		}else break; 	} 	if(head==NULL)  		head = tmpChain; } #endif


Question: C语言如何获取磁盘下最新创建的文件夹？

能具体说说该怎么写吗？从来没接触过这块，有点不知道 该怎么写

简单的办法就是 检索所有文件，然后读取文件日期。  还有就是拦截API


Question: 关于二级指针有关的赋值及作为参数的问题

理论上不可以有啥指回问题。 改变二级指针指向，只好用三级指针做形参，传递二级指针的地址。 C++可以用指针的引用做形参。 ，，，，，，，，，，，，，，，，， 话说，C,C++给程序员一定的灵活性， 代价是自己控制逻辑，自己检查问题， 编译器对这种灵活的技巧， 不支持，也不反对 全靠自己保证没问题。

对任何开发语言，都需要 自己控制逻辑，自己检查问题， 编译器对程序员使用灵活的技巧， 不支持，也不反对 全靠自己保证没问题。  区别仅仅是五十步笑百步而已。

明白了  引用 16 楼 lm_whales 的回复:。。。。。。。。。局部非静态变量的地址 在哪？？指针t吗？？

。。。。。。。。。局部非静态变量的地址

局部变量的地址， 不能通过返回值， 或者参数在函数调用结束后， 传递出函数供调用者使用，这样的话，会造成， 使用结束了生命周期的变量的内存（地址）的错误，结果未定义 这里未定义，有几种情况 1）仅仅为何输出地址，当然不会有错误了，       然而很难控制仅仅输出地址，而不是去读写。而且必要性也不大。       或者弃而不用。这同样是说的好听，很难做到。       所以，不应该返回这个变量的地址，       而不是返回了再控制不去使用，或者仅仅输出地址（或者其他诸如，比较地址）之用。  2）读取数据，可能会出错，这个数据有可能不正确，但是不一定会出错       未定义在这里的含义是，可能但未必出错，       最重要的是，不要这么做，因为结果不可靠。 3）写入数据，可能会出错，但是不一定会出错，然而一旦出错，就会很严重     写入的数据，很可能破坏了了堆栈的结构。导致程序不能正常执行。     未定义在这里的含义是，同样是，可能但未必出错，     但是这里问题，比读取要严重得多，程序可能会出现莫名其妙的错误，很难查找追踪。

指针是为了间接引用数据（不是变量） 如果指针指向 int,double ,struct 等数据类型 我们说它是一级指针 如果指向指针，我们说它是多级指针 这个级指的是间接引用次数 通过多少次间接引用，可以指向 int,double ,struct 等数据类型。就说这个指针是多少级指针。 int *p;//一级指针 int **pp;//二级指针   int ***ppp;//三级指针 int n=10; int *p=&n; //从指针p到 n ，也就是 *p 是一次间接引用，*p就是 n了。 int **pp=&p;从指针 pp 到 p,再到 n，是两次间接引用，*pp 就是 p ,*p 就是 n ,所以 **pp就是 n;pp是二级指针。 多级指针，就是这回事。 从定义看，就是*号的个数。 当然这是概略的，不够严谨的表述。  更复杂的情况，要更加细致的描述，但是初学指针，这就够了

引用 18 楼 lm_whales 的回复:看错了，这里没有传递过  局部非静态变量的地址。   #include<stdio.h> int k=7,m=5; void f(int **s) { int *t=&k;//局部非静态变量指针 t，指向全局变量k没有问题 s=&t;*s=&m; //s=&t:只能在函数内部起作用，出了函数，实参指针r的值，没变 printf("%d,%d,%d\n",k,*t,**s);//这地方没问题。输出7,5,5 //t=&k;这句不起作用。 //因为 *s 就是t ; //*s=&m ;改变了t的值，结果就是 t 的值是m的地址 ,所以,*t,和**s 都是m,所以输出 7,5,5 } int main() { int i=3,*p=&i,**r=&p; f(r);//r 的值不变，依然是 &p; printf("%d,%d,%d\n",i,*p,**r);//这地方 r 依然是&p } PS： 这个运行结果不奇怪 //运行结果7,5,5 3,3,3  #include<stdio.h> int k=7; void f(int **s) { int *t=&k; *s=t;//*s=t;这个结果影响了实参所指对象的值，因为实参和s 的值相同,他们指向的是同一块内存。         // 结果就是 *s 就是k的地址,因此，**s 就是 k;                 //传递指针的作用就是，使用，或者修改 *s,**s 等等，可以间接使用和修改的数据。         //这里指针参数的用法，完全没问题，不过直接写*s=&k;就可以了。         //这里*s =t 就是    于间接引用中的写，也就是修改。  printf("%d,%d,%d\n",k,*t,**s);//这里的输出，就属于间接引用中的（读取）使用。 } main() { int i=3,*p=&i,**r=&p; // f(r);//函数调用结束,*r =&k; 所以， printf("%d,%d,%d\n",i,*p,**r);//输出3，7，7                                             // *r 被改变了，也就是 p 被改变了，                                            //指向k了，此时 *p 变成 &k了                                           //*p 是k,**r 也是k 所以输出3，7，7   } //运行结果7,7,7 3,7,7 谢谢，大致都懂了，就还有一个疑惑，就是二级指针s指向r是不是变三级指针还是四级指针。一级指针可以指回二级吗？

引用 19 楼 zhao4zhong1 的回复:理解和讨论之前请先学会如何观察！  计算机组成原理→DOS命令→汇编语言→C语言（不包括C++）、代码书写规范→数据结构、编译原理、操作系统→计算机网络、数据库原理、正则表达式→其它语言（包括C++）、架构……  对学习编程者的忠告： 多用小脑和手，少用大脑、眼睛和嘴，会更快地学会编程！ 眼过千遍不如手过一遍！ 书看千行不如手敲一行！ 手敲千行不如单步一行！ 单步源代码千行不如单步Debug版对应汇编一行！ 单步Debug版对应汇编千行不如单步Release版对应汇编一行！ 不会单步Release版对应汇编？在你想单步Release版C/C++代码片断的前面临时加一句DebugBreak();重建所有，然后在IDE中运行。（一般人我不告诉他！）  VC调试时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化，这样过一遍不就啥都明白了吗。 对VC来说，所谓‘调试时’就是编译连接通过以后，按F10或F11键单步执行一步以后的时候，或者在某行按F9设了断点后按F5执行停在该断点处的时候。 （Turbo C或Borland C用Turbo Debugger调试,Linux或Unix下用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。）  想要从本质上理解C指针，必须学习汇编以及C和汇编的对应关系。 从汇编的角度理解和学习C语言的指针，原本看似复杂的东西就会变得非常简单！ 指针即地址。“地址又是啥？”“只能从汇编语言和计算机组成原理的角度去解释了。” 但我又不得不承认：  有那么些人喜欢或者适合用“先具体再抽象”的方法学习和理解复杂事物；  而另一些人喜欢或者适合用“先抽象再具体”的方法学习和理解复杂事物。 而我本人属前者。  不要企图依赖输出指针相关表达式...的值【比如printf("%p\n",...);或者cout<<...】来理解指针的本质， 而要依赖调试时的反汇编窗口中的C/C++代码【比如void *p=(void *)(...);】及其对应汇编指令以及内存窗口中的内存地址和内存值来理解指针的本质。  这辈子不看内存地址和内存值；只画链表、指针示意图，画堆栈示意图，画各种示意图，甚至自己没画过而只看过书上的图……能从本质上理解指针、理解函数参数传递吗？本人深表怀疑！ 这辈子不种麦不收麦不将麦粒拿去磨面；只吃馒头、吃面条、吃面包、……甚至从没看过别人怎么蒸馒头，压面条，烤面包，……能从本质上理解面粉、理解面食吗？本人深表怀疑！！  提醒： “学习用汇编语言写程序” 和 “VC调试(TC或BC用TD调试)时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化，这样过一遍不就啥都明白了吗。 （Linux或Unix下可以在用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。） 想要从本质上理解C指针，必须学习C和汇编的对应关系。” 不是一回事！  不要迷信书、考题、老师、回帖； 要迷信CPU、编译器、调试器、运行结果。 并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。 任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实！  有人说一套做一套，你相信他说的还是相信他做的？ 其实严格来说这个世界上古往今来所有人都是说一套做一套，不是吗？  不要写连自己也预测不了结果的代码！  电脑内存或文件内容或传输内容只是一个一维二进制字节数组及其对应的二进制地址； 人脑才将电脑内存或文件内容或传输内容中的这个一维二进制字节数组及其对应的二进制地址的某些部分看成是整数、有符号数/无符号数、浮点数、复数、英文字母、阿拉伯数字、中文/韩文/法文……字符/字符串、汇编指令、函数、函数参数、堆、栈、数组、指针、数组指针、指针数组、数组的数组、指针的指针、二维数组、字符点阵、字符笔画的坐标、黑白二值图片、灰度图片、彩色图片、录音、视频、指纹信息、身份证信息……  十字链表交换任意两个节点C源代码（C指针应用终极挑战）http://download.csdn.net/detail/zhao4zhong1/5532495    大哥，你这么有才，都可以出书了。 我现在是半路出家，你不是说不要迷信书本，按照你那些看下去，可以吗？ 还有就是你有一点我不认同，必须是从抽象到具体，因为你调试什么也要想到东西才可能调试，要不就是拿课本上的案例，不能老是那人家已经弄好的理论，那样就不可能发现新的问题，不断进步。 那些理论的书有什么推荐？

终于明白了一点，不同类型间相互付地址即指针指向要用&号(二级指针类型和一级指针类型不同) 所以才有s=&t;类似一级指针指向变量时的t=&k;其中*s=t是因为两个都是同类型，不用&可以相互赋值，类似于相同类型变量赋值。  但是还是有一个问题就是两个形参**s都关联了r，为什么结果却大不相同？

取地址符和星号可以说是互逆运算，加了星号代表值，加了取地址符表示地址打印出来就像一串乱码

*s就是一级指针，还是相当于地址，只有两个星号的时候才表示值，可以改变一个星号指的地址上面的值

int **s是二级指针

引用 7 楼 qq_36048429 的回复:楼主明白了吗？应该不难，〖前提是我没看错什么〗 你说说看，看我能捞到什么自己遗漏的知识点

引用 7 楼 qq_36048429 的回复:楼主明白了吗？应该不难，〖前提是我没看错什么〗  不明白，头都爆了，新手勿喷

楼主明白了吗？应该不难，〖前提是我没看错什么〗

引用 5 楼 cbaggdd66的回复:[quote=引用 1 楼 user-4 的回复:] 二给指针就是指向一级指针的指针  s=&t 就是取t的地址，t是一个一级指针，&t得到一个二级指针  不用&类型不同竟然会报错，一个是int*，一个是int** 不同类型的两个地址是不是不能相互赋值，还是用&就可以随便赋值？[/quote] 是的，1楼已经说了，二级指针就是指向一级指针的指针

引用 1 楼 user-4 的回复:二给指针就是指向一级指针的指针  s=&t 就是取t的地址，t是一个一级指针，&t得到一个二级指针  不用&类型不同竟然会报错，一个是int*，一个是int** 不同类型的两个地址是不是不能相互赋值，还是用&就可以随便赋值？

引用 1 楼 user-4 的回复:二给指针就是指向一级指针的指针  s=&t 就是取t的地址，t是一个一级指针，&t得到一个二级指针  不用&类型不同竟然会报错，一个是int*，一个是int**   那s=&t中的s代表的是什么？&t不是得到t的地址？然后付给s？


Question: 菜鸟求助，这段程序在VS2015上运行通过，为什么在GCC下提示RuntimeErr

char s1[100], s2[100], s3[100] = { 0 };  这些东西移到外面    double len = strlen(s1); 这个你一个数组长度需要用double么？   for (i = 0; i < len && i<100; i++) 必须要加上这个防止数组越界的东西。要是测试数据是1000个字符，你这东西分分钟被爆掉

可以的，大不了开多点内存，内存不会多多少

输入是否保证在 99 个字符之内？

int main()是不可以有void

引用 1 楼 user-4 的回复:不要用在线的。。。.是做在线测试，又没用过用本地GCC，找工作的苦逼党啊

不要用在线的。。。.


Question: 关于字符串区地址的问题。

理解和讨论之前请先学会如何观察！  计算机组成原理→DOS命令→汇编语言→C语言（不包括C++）、代码书写规范→数据结构、编译原理、操作系统→计算机网络、数据库原理、正则表达式→其它语言（包括C++）、架构……  对学习编程者的忠告： 多用小脑和手，少用大脑、眼睛和嘴，会更快地学会编程！ 眼过千遍不如手过一遍！ 书看千行不如手敲一行！ 手敲千行不如单步一行！ 单步源代码千行不如单步Debug版对应汇编一行！ 单步Debug版对应汇编千行不如单步Release版对应汇编一行！ 不会单步Release版对应汇编？在你想单步Release版C/C++代码片断的前面临时加一句DebugBreak();重建所有，然后在IDE中运行。（一般人我不告诉他！）  VC调试时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化，这样过一遍不就啥都明白了吗。 对VC来说，所谓‘调试时’就是编译连接通过以后，按F10或F11键单步执行一步以后的时候，或者在某行按F9设了断点后按F5执行停在该断点处的时候。 （Turbo C或Borland C用Turbo Debugger调试,Linux或Unix下用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。）  想要从本质上理解C指针，必须学习汇编以及C和汇编的对应关系。 从汇编的角度理解和学习C语言的指针，原本看似复杂的东西就会变得非常简单！ 指针即地址。“地址又是啥？”“只能从汇编语言和计算机组成原理的角度去解释了。” 但我又不得不承认：  有那么些人喜欢或者适合用“先具体再抽象”的方法学习和理解复杂事物；  而另一些人喜欢或者适合用“先抽象再具体”的方法学习和理解复杂事物。 而我本人属前者。  不要企图依赖输出指针相关表达式...的值【比如printf("%p\n",...);或者cout<<...】来理解指针的本质， 而要依赖调试时的反汇编窗口中的C/C++代码【比如void *p=(void *)(...);】及其对应汇编指令以及内存窗口中的内存地址和内存值来理解指针的本质。  这辈子不看内存地址和内存值；只画链表、指针示意图，画堆栈示意图，画各种示意图，甚至自己没画过而只看过书上的图……能从本质上理解指针、理解函数参数传递吗？本人深表怀疑！ 这辈子不种麦不收麦不将麦粒拿去磨面；只吃馒头、吃面条、吃面包、……甚至从没看过别人怎么蒸馒头，压面条，烤面包，……能从本质上理解面粉、理解面食吗？本人深表怀疑！！  提醒： “学习用汇编语言写程序” 和 “VC调试(TC或BC用TD调试)时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化，这样过一遍不就啥都明白了吗。 （Linux或Unix下可以在用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。） 想要从本质上理解C指针，必须学习C和汇编的对应关系。” 不是一回事！  不要迷信书、考题、老师、回帖； 要迷信CPU、编译器、调试器、运行结果。 并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。 任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实！  有人说一套做一套，你相信他说的还是相信他做的？ 其实严格来说这个世界上古往今来所有人都是说一套做一套，不是吗？  不要写连自己也预测不了结果的代码！  电脑内存或文件内容或传输内容只是一个一维二进制字节数组及其对应的二进制地址； 人脑才将电脑内存或文件内容或传输内容中的这个一维二进制字节数组及其对应的二进制地址的某些部分看成是整数、有符号数/无符号数、浮点数、复数、英文字母、阿拉伯数字、中文/韩文/法文……字符/字符串、汇编指令、函数、函数参数、堆、栈、数组、指针、数组指针、指针数组、数组的数组、指针的指针、二维数组、字符点阵、字符笔画的坐标、黑白二值图片、灰度图片、彩色图片、录音、视频、指纹信息、身份证信息……  十字链表交换任意两个节点C源代码（C指针应用终极挑战）http://download.csdn.net/detail/zhao4zhong1/5532495

str和&str虽然在数值上是一样的（都是数组首地址），但两者的含义是不同的

引用 4 楼 zirendonghe1748的回复:[quote=引用 2 楼 paschen 的回复:] &str是指向数组的指针，+1移动的是一个数组的大小，而不是数组中一个元素的大小  那什么时候是移动一个数组，什么时候是移动一个元素呢？[/quote] 取决于你指针的类型， 指针类型是int，就移一个int大小，是数组就移一个数组大小

引用 2 楼 paschen 的回复:&str是指向数组的指针，+1移动的是一个数组的大小，而不是数组中一个元素的大小  那什么时候是移动一个数组，什么时候是移动一个元素呢？

char *p=(char*)(&str+1);代码中的&str+1所取的是数组str的首地址，再在这个地址的值基础上加上sizeof(str)的值，也就是下一个数组的首地址，即当前这个指针已经已经越过了数组str的界限，其相当于指向str数组中最后一个地址的下一个空地址，而求的(*（p-1）)实际上是对*p指针进行减1操作，得到的是上一个元素的地址所对应的元素，即str数组的最后一个地址处存放的元素d.这是个人的见解，希望对你有用，如评论有哪些不足之处望大家指点，谢谢！

str 是一个数组 &str 得到的是一个指向数组的指针 &str + 1 将向后移动一个数组的大小


Question: 如何把256进制的数组转化成10进制的

这个问题，我参考了把10进制转换成2进制的方法。把每个数字都除以10，余数乘以256加上下一位，一直到最终的余数出现，则是结果的一位，这样不断的下去可以得到任意位数的。这是对于整数，如果对于小数部分，则是每一位乘以10，然后把进位到小数点之前的数字取出来就可以了。

可以除以100 或者1000，10000 得到百千万进制数。 直接输出，不足补零，就是10进制数了。

小数点左边每一位： 256^0 * 第一位 + 256 ^ 1 * 第二位 ................ 小数点右边每一位：256^-1 * 右第一位  ............. 然后加起来就是最终结果

20个字节（160位）的大数，小数点右移10个字节（80位），就变成160位的大整数了。 搜大整数


Question: VS2013 C语言为什么变量定义要在任何语句之前？

vs不能修改(自定义)编译器吗？

可以将函数以及变量声明写到一个头文件中，然后在需要引用的源码文件头部包含这个头文件，这样一般就不会有问题。 不过，出于数据隐藏的考虑，一般只把函数声明写到头文件中。所有变量声明还是老老实实写到源码头部比较好，不容易出错。

2010 还不支持，2015 已经支持了 2013 或许也支持吧

引用 9 楼 zh1599512 的回复:[quote=引用 4 楼 SXJIAKE 的回复:] C99 支持随时声明。只是 MSC 这个垃圾连这个基本特性一直都不支持，搞的 c 扩展名要编译通过得大改动。根本没指望它能支持其他 C99 特性，如 stdbool.h 等。只要它支持 for 内部声明变量，以及变量可以随时声明这两个特性我就满意了。 对的，ms真是太喜欢自己搞一套。反正最近写c用ms是醉了[/quote] MS 遵守的是 C89 啊，又不是自己弄得标准。

引用 4 楼 SXJIAKE 的回复:C99 支持随时声明。只是 MSC 这个垃圾连这个基本特性一直都不支持，搞的 c 扩展名要编译通过得大改动。根本没指望它能支持其他 C99 特性，如 stdbool.h 等。只要它支持 for 内部声明变量，以及变量可以随时声明这两个特性我就满意了。 对的，ms真是太喜欢自己搞一套。反正最近写c用ms是醉了

这个是C语言的旧标准吧

进即退 得即舍

C 对goto语句，还是适当支持的 因此，在函数开头定义变量， 对于goto 的好处就是不会条过变量定义 C++ 支持随处定义变量，但是goto 语句，就比较难用了  其他： 对于C++ 如果不支持到处定义，变量 那么每个类都可能需要另外，定义一个init 之类的函数来初始化数据。 因为定义对象，可能会使用别的变量来初始化它。 坚持在开头定义变量，会很不方便。 试想，  一个构造函数参数为 int   int a=10; C c1(a); a=11; C c2(a); 这样便计算便定义变量，是不是很方便 如果非要写在 开头 就会很麻烦

问题根源：编译器问题——C89和C99 C89规定，在任何执行语句之前，在块的开头声明所有局部变量。 在C99以及C++中则没有这个限制，即在首次使用之前，可在块的任何位置都可以声明变量。  不同风格利弊： 变量在函数开头定义：便于修改 变量就近定义：便于阅读 变量在块语句的开头定义：有的临时变量放在语句块开头，可以不用在函数开头定义，并且在使用完毕后释放空间，可以这么做，但不要无故添加花括号来提高代码的结构。

以前C标准这么规定的，没有原因

C语言以前是这样的，变量定义必须在任何语句之前，但是现在不是C语言已经和C++一样了，可以随时定义随用调用

以前的 C 是这样要求的。  换 VS2015 试试？


Question: 指针的一个问题

对((int*)h)++操作的时候，系统不知道h的类型，不知道加多少字节，错误的地方在于void* h = NULL; 将其改为int* h = NULL;程序就正确了

//char (*(*x[3])())[5];//x是什么类型的变量? // //分析C语言声明，关键是搞清楚这个变量是个什么东西（函数、指针、数组）， //是函数那么剩下的就是他的参数和返回值， //是指针那剩下部分是说明他指向什么， //是数组剩下的部分就是说明数组的成员是什么类型。 //解析C语言声明规则： //从左侧第一个标识符开始，按照优先级进行结合。*表示是..的指针，const表示只读的，volatile表示可变的，[]表示是数组，()表示是函数。 // //x和[3]结合说明是一个大小为3的数组，该数组的每个元素为一类指针，该类指针指向一类函数，该类函数无参数，返回一类指针，该类指针指向一个大小为5的char型数组 #include <stdio.h> #include <typeinfo.h> char num[5]; char (*x00())[5] {     return # } int main() {     char (*(*x[3])())[5];//是个数组，大小为3     char (*(*x0  )())[5];//数组的元素，是个函数指针     char (*( x00 )())[5];//函数原型，参数为空，返回值为指针     char (*  x000   )[5];//返回值      x0 = x00;     x[0] = x0;     x[1] = x0;     x[2] = x0;     printf("typeid(x).name() is %s\n",typeid(x).name());     return 0; } //typeid(x).name() is char (* (__cdecl**)(void))[5]

&num是一个常量值，常量值不能++

h = ((int*)h)++;  //***** 这个 (int*)h) 运输结果是一个常量，常量不支持++操作。

闲着不也是闲着吗？

C/C++语言中可以放在赋值符号左边的变量，即具有对应的可以由用户访问的存储单元，并且能够由用户去改变其值的量。左值表示存储在计算机内存的对象，而不是常量或计算的结果。或者说左值是代表一个内存地址值，并且通过这个内存地址，就可以对内存进行读并且写（主要是能写）操作；这也就是为什么左值可以被赋值的原因了。相对应的还有右值：当一个符号或者常量放在操作符右边的时候，计算机就读取他们的“右值”，也就是其代表的真实值。简单来说就是，左值相当于地址值，右值相当于数据值  http://blog.csdn.net/zlhy_/article/details/8349300

引用 5 楼 zhao4zhong1 的回复:理解和讨论之前请先学会如何观察！  计算机组成原理→DOS命令→汇编语言→C语言（不包括C++）、代码书写规范→数据结构、编译原理、操作系统→计算机网络、数据库原理、正则表达式→其它语言（包括C++）、架构……  对学习编程者的忠告： 多用小脑和手，少用大脑、眼睛和嘴，会更快地学会编程！ 眼过千遍不如手过一遍！ 书看千行不如手敲一行！ 手敲千行不如单步一行！ 单步源代码千行不如单步Debug版对应汇编一行！ 单步Debug版对应汇编千行不如单步Release版对应汇编一行！ 不会单步Release版对应汇编？在你想单步Release版C/C++代码片断的前面临时加一句DebugBreak();重建所有，然后在IDE中运行。（一般人我不告诉他！）  VC调试时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化，这样过一遍不就啥都明白了吗。 对VC来说，所谓‘调试时’就是编译连接通过以后，按F10或F11键单步执行一步以后的时候，或者在某行按F9设了断点后按F5执行停在该断点处的时候。 （Turbo C或Borland C用Turbo Debugger调试,Linux或Unix下用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。）  想要从本质上理解C指针，必须学习汇编以及C和汇编的对应关系。 从汇编的角度理解和学习C语言的指针，原本看似复杂的东西就会变得非常简单！ 指针即地址。“地址又是啥？”“只能从汇编语言和计算机组成原理的角度去解释了。” 但我又不得不承认：  有那么些人喜欢或者适合用“先具体再抽象”的方法学习和理解复杂事物；  而另一些人喜欢或者适合用“先抽象再具体”的方法学习和理解复杂事物。 而我本人属前者。  不要企图依赖输出指针相关表达式...的值【比如printf("%p\n",...);或者cout<<...】来理解指针的本质， 而要依赖调试时的反汇编窗口中的C/C++代码【比如void *p=(void *)(...);】及其对应汇编指令以及内存窗口中的内存地址和内存值来理解指针的本质。  这辈子不看内存地址和内存值；只画链表、指针示意图，画堆栈示意图，画各种示意图，甚至自己没画过而只看过书上的图……能从本质上理解指针、理解函数参数传递吗？本人深表怀疑！ 这辈子不种麦不收麦不将麦粒拿去磨面；只吃馒头、吃面条、吃面包、……甚至从没看过别人怎么蒸馒头，压面条，烤面包，……能从本质上理解面粉、理解面食吗？本人深表怀疑！！  提醒： “学习用汇编语言写程序” 和 “VC调试(TC或BC用TD调试)时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化，这样过一遍不就啥都明白了吗。 （Linux或Unix下可以在用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。） 想要从本质上理解C指针，必须学习C和汇编的对应关系。” 不是一回事！  不要迷信书、考题、老师、回帖； 要迷信CPU、编译器、调试器、运行结果。 并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。 任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实！  有人说一套做一套，你相信他说的还是相信他做的？ 其实严格来说这个世界上古往今来所有人都是说一套做一套，不是吗？  不要写连自己也预测不了结果的代码！  电脑内存或文件内容或传输内容只是一个一维二进制字节数组及其对应的二进制地址； 人脑才将电脑内存或文件内容或传输内容中的这个一维二进制字节数组及其对应的二进制地址的某些部分看成是整数、有符号数/无符号数、浮点数、复数、英文字母、阿拉伯数字、中文/韩文/法文……字符/字符串、汇编指令、函数、函数参数、堆、栈、数组、指针、数组指针、指针数组、数组的数组、指针的指针、二维数组、字符点阵、字符笔画的坐标、黑白二值图片、灰度图片、彩色图片、录音、视频、指纹信息、身份证信息……  十字链表交换任意两个节点C源代码（C指针应用终极挑战）http://download.csdn.net/detail/zhao4zhong1/5532495  你天天复制这玩意有意思吗

返回值为指向整型指针的指针，后再对这个指针自增加一，你这感觉是对h指针赋地址两次，一次先把num=1的首地址赋给了h指针，后有把另一个指针指的地址指向h，后在对其自加一，根据运算符的优先级，自增运算符比赋值运算符高，而且自增++运算符好像只能作为右值，不知道这样对木？只是自己的一点看法，如有错望指点，谢谢！

#include<cstdio> int main() { 	int num=1; 	void* h = NULL; 	h = # 	int* tmp = (int*)h; 	h = tmp++;  //***** 	printf("%x", h); }

因为 你的 ++ 运算需要左值  而 (int*)h 得到的是一个右值


Question: win32 api CreateFont不起作用

这里有很多问题： 1. hdc = BeginPaint(hWnd, &ps); 都返回 hdc 了为何不用，而是反复的 GetDC？ 请用 BeginPaint 返回的 hdc，或者 ps.hdc。 2. GetDC 也需要对应的 ReleaseDC。你三次调用 GetDC 都释放了吗？ 3. CreateFont 反复创建没关系，但是你没有对用 DeleteObject 去销毁。 最好在 WM_CREATE、WM_INITDIALOG 中创建，在 WM_SETFONT 中变更，在 WM_NCDESTROY 中销毁。

引用 8 楼 SXJIAKE 的回复:4. SelectObject 的使用完毕都要恢复原值 HFONT hFont; HFONT hFontOld; switch (uMsg) { // ...... case WM_PAINT: 	BeginPaint(hWnd, &ps); 	hFont = CreateFontW(......); 	hFontOld = SelectObject(ps.hdc, hFont); 	SetTextColor(ps.hdc, 0xff0000); 	TextOut(ps.hdc, 10, 10, L"hello world", 11); 	SelectObject(ps.hdc, hFontOld); 	DeleteObject(hFont); 	EndPaint(hWnd, &ps); 	break; // ...... } 是的，这些问题都没注意到，感谢回答

https://msdn.microsoft.com/en-us/library/dd183499.aspx 的例子中，最后有一句 DeleteObject(hFont); 将CreateFontW创建的对象给释放了。   如果你的程序，不使用多种多样的字体，你可以将字体对象事先创建出来，在WM_PAINT中使用即可。

系统有没有自带Comic Sans MS字体？

引用 1 楼 user-4 的回复:检查函数返回值看是否成功  https://msdn.microsoft.com/en-us/library/dd183499.aspx 按照这个例子是可以成功的，是HDC的问题，GetDC(hWnd)会获得不同的HDC对象，而我的代码没有使用一个HDC对象。而是多次使用GetDC(hWnd)

引用 2 楼 60 的回复:CreateFontW 不应该在WM_PAINT中执行，WM_PAINT事件时时刻刻在触发，相当于CreateFontW 执行了无数次。  CreateFontW 执行次数过多，将因为资源不足，导致创建失败。就是这个“GDI”对象不足。   是的，不能直接放入WM_PAINT中执行，不过放在hdc = BeginPaint(hWnd, &ps);和EndPaint(hWnd, &ps);则不会产生大量的GDI对象不足的问题。https://msdn.microsoft.com/en-us/library/dd183499.aspx 按照官方这个例子是可以的，在其他地方我没成功，还是对GDI的理解不足啊。


Question: 菜鸟求助，这段C程序用VS2015编译通过且运行正确，为何用GCC调试却说runtimeErr

引用 2 楼 paschen 的回复:GCC下出错了中断下来，判断出错原因我不会用GCC啊，也没用过。这是在线测试，在线测试用的是GCC，我是本地用VS编译的，然后直接复制上去，然后就提示runtimeErr

GCC下出错了中断下来，判断出错原因

VS下运行没出错不代表一定正确

引用 6 楼 xusongge 的回复:引用 4 楼 user-2 的回复:把 system("pause") 去掉。大神再问你一个问题，VS下引用math.h头文件就可以调用sqrt函数，但是在GCC下就不行，提示找不到sqrt，该怎么办？  请提供完整的程序

引用 4 楼 user-2 的回复:把 system("pause") 去掉。太感谢你了！！果然是这个问题，去掉就AC了

把 system("pause") 去掉。


Question: “读信息个数未知的文件，写入数组”，有木有好的解决方案呢？

malloc,realloc,free

引用 11 楼 zhao4zhong1 的回复:malloc,realloc,free改天试试看

引用 9 楼 sxldfang 的回复:[quote=引用 楼主 shiyuzuxiaqianli 的回复:] 比方说，一个文件中有很多（事先不知道个数）正整数，现在打算把它们读到数组中。我的想法是，先计算出个数，再开一个数组读取数字。问一下有没有别的方案；或者直接就放弃用数组，使用别的什么方法。优先考虑C语言  链表+动态分配[/quote]看来只要放弃数组，就有好的方法

引用 楼主 shiyuzuxiaqianli 的回复:比方说，一个文件中有很多（事先不知道个数）正整数，现在打算把它们读到数组中。我的想法是，先计算出个数，再开一个数组读取数字。问一下有没有别的方案；或者直接就放弃用数组，使用别的什么方法。优先考虑C语言  链表+动态分配

自己顶起来，自己顶起来

引用 6 楼 user-5 的回复:别存数组了，边读边处理 边读边处理？   你指的是读取一个处理一个 然后在读取？ 如果是这样 效率低下啊，还不如直接全部一次性读取到内存里在处理

别存数组了，边读边处理

引用 4 楼 paschen的回复:[quote=引用 2 楼 千行百行的回复:][quote=引用 1 楼 paschen的回复:]方法一：先计算个数  方法二：采用类似C++STL中容器的实现，可以先预先分配适当大小内存，不够时分配更大的，然后将之前内容复制到新内存，释放旧内存 方法一就是我说到的方法啊，方法二感觉不是很好啊[/quote] 使用文件映射方式，然后将将指针指向数据开头，就可以直接按数组方式访问了[/quote] 不是很懂版主说的这种方法，查查看去

引用 2 楼 千行百行的回复:[quote=引用 1 楼 paschen的回复:]方法一：先计算个数  方法二：采用类似C++STL中容器的实现，可以先预先分配适当大小内存，不够时分配更大的，然后将之前内容复制到新内存，释放旧内存 方法一就是我说到的方法啊，方法二感觉不是很好啊[/quote] 使用文件映射方式，然后将将指针指向数据开头，就可以直接按数组方式访问了

自己顶上去，自己顶上去。

引用 1 楼 paschen的回复:方法一：先计算个数  方法二：采用类似C++STL中容器的实现，可以先预先分配适当大小内存，不够时分配更大的，然后将之前内容复制到新内存，释放旧内存 方法一就是我说到的方法啊，方法二感觉不是很好啊


Question: 关于浮点数表示的问题

引用 8 楼 user-2 的回复:假设尾数nBits,符号位1Bits，阶码m bits 其中符号1bits 最小正数为1 *2^-(2^(m-1)) *k 最大正数为 (2^n -1) *2^(2^(m-1))*k 如果尾数全部是小数 k=2^-n 如果尾数是1Bits 整数 nBits 尾数 k=1 如果按照IEEE 编码最小数 k=2^-n,最大数 k=1 大约是这样 具体看IEEE浮点数非常感谢 也谢谢版主

16Bits浮点数也许有，不过没见过 现在浮点数 一般有三种32Bits，64Bits，80Bits  这是X86对应浮点处理器X87,支持的浮点数格式 其他还有40Bits (DSP)，48Bits(pascal 语言，曾经支持的类型为real，或者real48) 32，64 参考IEEE浮点数 浮点数有些编码需要定义好它的意义 比如 曾经IBM浮点数，就定义指数为16进制的 所以，不指定具体格式，是不能说浮点数到底如何编码的  另外，还有一种定点小数编码。这是用整数当作小数用 十进制的话可以为4位小数 十六进制，也可以为4Bits 小数，这都是根据需要确定的

请讲的详细一点，真的不太懂

引用 6 楼 user-2 的回复:需要考虑，要不要隐藏位 IEEE是有隐藏位的应该没有 就那个题目而言。。能帮忙解答一下么

需要考虑，要不要隐藏位 IEEE是有隐藏位的

来人帮忙解答一下题目。。。。

引用 3 楼 paschen 的回复:http://www.cnblogs.com/xkfz007/archive/2012/02/27/2370357.html版主。能把我的那个题目给解释一下么。。。


Question: 有个算法问题求教！想不出来！

给定的是什么呢？

仅供参考：//实现一个函数：求数轴上多个线段覆盖的区域对应的多个线段; 输入多个线段,求出被这些线段覆盖的所有区域(也用线段表示); 一条线段用两个值表示(x0,x1), 其中x1>x0; //比如(1,3);(2,4);(5,6);结果为(1,4);(5,6); //比如(1,3);(2,4);(5,6);(4,7);结果为(1,7); #include <stdio.h> #define MAXLINES 100 struct LINE {     int x0;//左端     int x1;//右端 } ls[MAXLINES]; int i,j,k,n,x0,x1,c; void add() {     if (0==n) {//加入第一对点         ls[n].x0=x0;         ls[n].x1=x1;         n=1;     } else {         if (x1==ls[0].x0) {//右端＝第一条线段的左端             ls[0].x0=x0;//第一条线段的左端修改为左端             return;         }         if (x1<ls[0].x0) {//右端＜第一条线段的左端             for (i=n-1;i>=0;i--) {//将所有线段往后移                 ls[i+1].x0=ls[i].x0;                 ls[i+1].x1=ls[i].x1;             }             ls[0].x0=x0;//新第一条线段为x0,x1             ls[0].x1=x1;             n++;             return;         }         if (ls[n-1].x1<x0) {//左端＞最后那条线段的右端             ls[n].x0=x0;//新最后那条线段             ls[n].x1=x1;             n++;             return;         }         if (x0<=ls[0].x0 && ls[n-1].x1<=x1) {//左端≤第一条线段的左端且最后那条线段的右端≤右端             ls[0].x0=x0;//只剩这一条线段             ls[0].x1=x1;             n=1;             return;         }         for (i=0;i<n;i++) {//从左到右依次检查每条线段             if (ls[i].x0<=x0 && x0<=ls[i].x1) {//第i条线段的左端≤左端≤第i条线段的右端                 for (j=i;j<n;j++) {//从第i条线段开始从左到右依次检查每条线段                     if (ls[j].x0<=x1 && x1<=ls[j].x1) {//第j条线段的左端≤右端≤第j条线段的右端                         ls[i].x1=ls[j].x1;//第i条线段的右端改为第j条线段的右端                         for (k=1;k<=n-j-1;k++) {//将剩余线段往前移                             ls[i+k].x0=ls[j+k].x0;                             ls[i+k].x1=ls[j+k].x1;                         }                         n-=j-i;                         return;                     }                     if (ls[j].x1<x1 && (j==n-1 || x1<ls[j+1].x0)) {//第j条线段的右端＜右端＜第j+1条线段的左端(如果有)                         ls[i].x1=x1;//第i条线段的右端改为右端                         for (k=1;k<=n-j-1;k++) {//将剩余线段往前移                             ls[i+k].x0=ls[j+k].x0;                             ls[i+k].x1=ls[j+k].x1;                         }                         n-=j-i;                         return;                     }                 }             }             if (ls[i].x1<x0 && (i==n-1 || x0<ls[i+1].x0)) {//第i条线段的右端＜左端＜第i+1条线段的左端(如果有)                 if (i!=n-1 && x1<ls[i+1].x0) {//右端＜第i+1条线段的左端(如果有)                     for (k=n-1;k>=i+1;k--) {//将从第i+1条线段开始的所有线段往后移                         ls[k+1].x0=ls[k].x0;                         ls[k+1].x1=ls[k].x1;                     }                     ls[i+1].x0=x0;//新第i+1条线段为x0,x1                     ls[i+1].x1=x1;                     n++;                     return;                 }                 for (j=i+1;j<n;j++) {//从第i+1条线段开始从左到右依次检查每条线段                     if (ls[j].x0<=x1 && x1<=ls[j].x1) {//第j条线段的左端≤右端≤第j条线段的右端                         ls[i+1].x0=x0;//第i+1条线段的左端改为左端                         ls[i+1].x1=ls[j].x1;//第i+1条线段的右端改为第j条线段的右端                         for (k=1;k<=n-j-1;k++) {//将剩余线段往前移                             ls[i+1+k].x0=ls[j+k].x0;                             ls[i+1+k].x1=ls[j+k].x1;                         }                         n-=j-i-1;                         return;                     }                     if (ls[j].x1<x1 && (j==n-1 || x1<ls[j+1].x0)) {//第j条线段的右端＜右端＜第j+1条线段的左端(如果有)                         ls[i+1].x0=x0;//第i+1条线段的左端改为左端                         ls[i+1].x1=x1;//第i+1条线段的右端改为右端                         for (k=1;k<n-j-1;k++) {//将剩余线段往前移                             ls[i+k].x0=ls[j+k].x0;                             ls[i+k].x1=ls[j+k].x1;                         }                         n-=j-i-1;                         return;                     }                 }             }             if (x0<ls[i].x0) {//左端＜第i条线段的左端                 for (j=i;j<n;j++) {//从第i条线段开始从左到右依次检查每条线段                     if (ls[j].x0<=x1 && x1<=ls[j].x1) {//第j条线段的左端≤右端≤第j条线段的右端                         ls[i].x0=x0;//第i条线段的左端改为左端                         ls[i].x1=ls[j].x1;//第i条线段的右端改为第j条线段的右端                         for (k=1;k<=n-j-1;k++) {//将剩余线段往前移                             ls[i+k].x0=ls[j+k].x0;                             ls[i+k].x1=ls[j+k].x1;                         }                         n-=j-i;                         return;                     }                     if (ls[j].x1<x1 && (j==n-1 || x1<ls[j+1].x0)) {//第j条线段的右端＜右端＜第j+1条线段的左端(如果有)                         ls[i].x0=x0;//第i条线段的左端改为左端                         ls[i].x1=x1;//第i条线段的右端改为右端                         for (k=1;k<=n-j-1;k++) {//将剩余线段往前移                             ls[i+k].x0=ls[j+k].x0;                             ls[i+k].x1=ls[j+k].x1;                         }                         n-=j-i;                         return;                     }                 }             }         }     } } void show() {     for (i=0;i<n;i++) {         printf("(%d,%d);",ls[i].x0,ls[i].x1);     }     printf("\n"); } void main() {     n=0;     c=0;     while (1) {         printf("Input x0,x1(x0<x1;0,0 to exit):");         fflush(stdout);         rewind(stdin);         if (2==scanf("%d,%d",&x0,&x1)) {             if (0==x0&&0==x1) break;             if (x1>x0) {                 c++;                 if (c>=MAXLINES) {                     printf("Up to %d!\n",MAXLINES);                     break;                 }                 add();                 show();             }         }     } }

看似简单中蕴含着复杂。

已知什么，如果这两个长方形各顶点坐标都知道有什么难求的

给定的长方形，有说明长方形的边平行这些条件没有，还是就是你图上的那种情况。   如果什么条件都没有的话，最少有0个重合，最多有8个重合点，是比较难计算的。

两个矩形，坐标用X11、X12、Y11、Y12和X21、X22、Y21、Y22表示，且Xi1<Xi2、Yi1<Yi2 当X11〈X21<X12<X22且Y11<Y21<Y12<Y22时两者有交点，交点为(X21,Y12)和(X12,Y21)


Question: 静态局部变量在内存中的位置什么时候分配内存

第一次调用时分配内存并做初始化。

参考PE加载器源代码。

(1) 静态局部变量在静态存储区内分配存储单元。在程序整个运行期间都不释放。而自动变量（即动态局部变量）属于动态存储类别，存储在动态存储区空间(而不是静态存储区空间)，函数调用结束后即释放。  (2) 为静态局部变量赋初值是在编译时进行值的，即只赋初值一次，在程序运行时它已有初值。以后每次调用函数时不再重新赋初值而只是保留上次函数调用结束时的值。而为自动变量赋初值，不是在编译时进行的，而是在函数调用时进行，每调用一次函数重新给一次初值，相当于执行一次赋值语句。   (3) 如果在定义局部变量时不赋初值的话，对静态局部变量来说，编译时自动赋初值0(对数值型变量)或空字符(对字符型变量)。而对自动变量来说，如果不赋初值，则它的值是一个不确定的值。这是由于每次函数调用结束后存储单元已释放，下次调用时又重新另分配存储单元，而所分配的单元中的值是不确定的。  (4) 虽然静态局部变量在函数调用结束后仍然存在，但其他函数是不能引用它的，也就是说，在其他函数中它是“不可见”的。

内存分配，C,C++是一致的，初始化未必一样 可以保证，静态变量第一次使用的时候，已经初始化了（或者就会初始化）。

已初始化的的全局变量和局部静态变量存放在.data段，未初始化的全局变量和局部静态变量一般存放在.bss段里，前者在生产可执行文件时就分配好内存了，后者在加载时才分配

对于C语言的全局和静态变量，其内存空间都是全局的  分配是在编译期，已经将变量放到了数据段了

应该和全局静态变量是一样的。


Question: C语言比C++特有的性质

引用 5 楼 libinden71 的回复:我记得还有大神说过：C++无非就是花架子，其实本质还是C  对于这句话 大家觉得呢？ 还有人说，语言只是语法不同而已。

比如C99提出的可变长数组，C++里没有

1）C 关键字最少 C++如今已经完全没有这个概念了，关键字很多很多了 C++关键字比C多多了 2）和其它语言互操作能力， C++ 对这个支持很少了，C++这个能力还是继承C的， 在面向对象，泛型上是跟着个背道而行的 3）面向底层，C++还保留，但在面向对象，泛型上，却是偏离这个能力的。 相比而言，C很纯粹 C++很复杂 虽然如此，也只是各有各的用途，并无好坏之分

varible-length array Complex types  _Generic Compound literals anonymous structure/union C 用的不多，欢迎补充

C语言本身只有很少的语法，语法非常的精简。


Question: 关于链表的问题 RE:无效内存引用

你的这句也不对： NODE *tem; tem->link = q->link->link->link; 你的tem都没有分配空间，指向的是未知区域（野指针），然后就进行赋值操作是会出现异常的。  单步调试和设断点调试（VS IDE中编译连接通过以后，按F10或F11键单步执行，按Shift+F11退出当前函数；在某行按F9设断点后按F5执行停在该断点处。）是程序员必须掌握的技能之一。

scanf("%d\n",&T);  改成  scanf("%d",&T);


Question: 关于链表处理数据的问题

仅供参考：//带表头结点的单向链表 #include <stdio.h> #include <stdlib.h> #include <malloc.h> #include <time.h> struct NODE {     int          data;     struct NODE *next; } H,*head,*p,*q,*q1,*s1,*s2,*s3,*s4,*s; int i,j,k,n,t,m; int main() {     srand(time(NULL));      //填写头节点数据     H.data=-1;     H.next=NULL;     head=&H;      //创建10个节点的单链表     p=head;     for (i=0;i<10;i++) {         q=(struct NODE *)malloc(sizeof(struct NODE));         if (NULL==q) return 1;         q->data=rand()%100;//填写0..99的随机值         q->next=NULL;         p->next=q;         p=q;     }      //输出整个单链表     s=head->next;     while (1) {         if (NULL==s) {             printf("\n");             break;         }         printf("%02d->",s->data);         s=s->next;     }      //将值为5的结点插入到单链表的第k个结点前     k=3;     n=0;     p=head;     while (1) {         if (NULL==p) {             break;         }         n++;         if (k==n) {             q=(struct NODE *)malloc(sizeof(struct NODE));             if (NULL==q) return 1;             q->data=5;             q->next=p->next;             p->next=q;             break;         }         p=p->next;     }      //输出整个单链表     s=head->next;     while (1) {         if (NULL==s) {             printf("\n");             break;         }         printf("%02d->",s->data);         s=s->next;     }      //删除第k个节点     k=5;     n=0;     p=head;     while (1) {         if (NULL==p) {             break;         }         n++;         if (k==n) {             q=p->next;             if (q) {                 p->next=q->next;                 free(q);             }             break;         }         p=p->next;     }      //输出整个单链表     s=head->next;     while (1) {         if (NULL==s) {             printf("\n");             break;         }         printf("%02d->",s->data);         s=s->next;     }      //从小到大排序     for (p=head;p!=NULL && p->next!=NULL;p=p->next) {         for (q=p->next;q!=NULL && q->next!=NULL;q=q->next) {             if (p->next->data > q->next->data) {                  //交换data //              printf("swap %02d %02d\n",p->next->data,q->next->data); //              t=p->next->data;p->next->data=q->next->data;q->next->data=t;                  //或者                  //交换next //              printf("swap %02d %02d\n",p->next->data,q->next->data);                 s1=p->next;                 s2=p->next->next;                 s3=q->next;                 s4=q->next->next;                  if (s2!=s3) {                      p->next=s3;                     s3->next=s2;                      q->next=s1;                     s1->next=s4;                 } else {                      p->next=s3;                     s3->next=s1;                            q=s3;                     s1->next=s4;                 }                  //输出整个单链表 //              s=head->next; //              while (1) { //                  if (NULL==s) { //                      printf("\n"); //                      break; //                  } //                  printf("%02d->",s->data); //                  s=s->next; //              } //              getchar();             }         }     }      //输出整个单链表     s=head->next;     while (1) {         if (NULL==s) {             printf("\n");             break;         }         printf("%02d->",s->data);         s=s->next;     }      //将整个链表逆序     if (head->next!=NULL && head->next->next!=NULL) {         p=head->next;         q=p->next;         p->next=NULL;         while (1) {             q1=q->next;             q->next=p;             p=q;             q=q1;             if (NULL==q) break;         }         head->next=p;     }      //输出整个单链表     s=head->next;     while (1) {         if (NULL==s) {             printf("\n");             break;         }         printf("%02d->",s->data);         s=s->next;     }      //将单链表中前 m 个结点和后 n 个结点进行互换,m+n为链表总长10     m=4;     n=6;     k=0;     p=head;     while (1) {         if (NULL==p) {             break;         }         k++;         if (m+1==k) {             q=p;         }         s=p;         p=p->next;     }     s1=head->next;     head->next=q->next;     s->next=s1;     q->next=NULL;      //输出整个单链表     s=head->next;     while (1) {         if (NULL==s) {             printf("\n");             break;         }         printf("%02d->",s->data);         s=s->next;     }      //释放所有节点     p=head->next;     while (1) {         if (NULL==p) {             break;         }         q=p->next;         free(p);         p=q;     }      return 0; } //84->28->20->23->96->19->59->97->29->41-> //84->28->05->20->23->96->19->59->97->29->41-> //84->28->05->20->96->19->59->97->29->41-> //05->19->20->28->29->41->59->84->96->97-> //97->96->84->59->41->29->28->20->19->05-> //41->29->28->20->19->05->97->96->84->59-> //

如楼上所说，你这个create函数逻辑有问题。 楼主可以自己单步调试，或者写个print函数，每次输入一个数据后就print一下整个链表，这样就知道自己的操作有没有问题了。 单步调试和设断点调试（VS IDE中编译连接通过以后，按F10或F11键单步执行，按Shift+F11退出当前函数；在某行按F9设断点后按F5执行停在该断点处。）是程序员必须掌握的技能之一。

下面的地方貌似有问题     struct number *creat()  //创建链表，储存数据 { struct number *head=0,*p1=0,*p2=0; head=p1=p2=(struct number*)malloc(LEN);  //开辟节点 fflush(stdin);    //清空输入缓存区 head=p1; p1->num=getch();  //输入数据 putchar(p1->num); while(p1->num!=N) { p1->next=p2;            //这个时候p2 == head的，也就是p1->next 是指向自己。 p1=p2; p2=(struct number*)malloc(LEN);  //开辟新节点          //给p2开辟了新空间，但是没有往里面赋值。p2的内容也没有挂在p1->next上。 fflush(stdin);   p1->num=getch();  //输入新数据                      //重新刷新p1的num值？ 应该是p2吧。 putchar(p1->num); } p1->next=0;  //尾节点清零 p2=0; return head;  //返回头节点 }

单步跟踪程序运行，观察分析原因


Question: c语言 关于 自增 后缀运算

一般来说： c=(a++)+(a++)+(a++);//语句1===> 5 + 6 + 7 = 18（后缀++是返回加一前的值） c=(++b)+(++b)+(++b);//语句2===>6 + 7 + 8 = 21（前置++ 是返回加一后的值）

前一个 从 15～21

前一个 从 15～18 后一个 从18～24 都是对的 关键是 第一个式子有三个表达式 a++   第二个式子有三个表达式 ++b 以任何顺序估值a++或者 ++b 这个表达式都对。 标准没规定，编译器随便安排。 这就是所谓的未定义

没有步骤，没有原理。 即使是同一个编译器的不同版本，结果都可能不同。 把讲这个东西的书扔掉吧


Question: strcpy(&url[i], '\0')或者url[len - i] = '\0'有错？

在这里把我找到的资料分享出来，方便遇到同样问题的学习者。 首先是能正常运行的程序如下： #include <stdio.h> #include <string.h>  void remove_filename(char *url) {     int i = 7;    //字符"http://"共7个字符，避免遇到这里的'/'      while(url[i] != '/')         i++;      char p[1] = "\0";     strcpy(&url[i], p);    //这里的修改可以看第一个链接的最后      puts(url); }  void remove_filename_2(char *url) {     int i, len = strlen(url);      for (i = 0; i <= len; i++)         if (url[len - i] == '/') {             url[len - i] = '\0';             break;         }      puts(url); }  int main(void) {     char str[40] = "http://www.knking.com/index.com";      remove_filename(str);     printf("\n"); //    remove_filename_2(str);    //这里不能同时调用      return 0; } 资料： 文字常量区、字符串常量、地址——来自CSDN-静革 字符串常量到底存放在哪个存储区——来自CSDN-daiyutage 字符串常量与const常量内存区（——选自陈皓的博客）——来自博客园-弱水三千12138（这里没能找到原文）

感谢版主的指点！ 已通过版主的指点找资料解决。

你的字符串是在常量区的，不能修改  如果要修改，应用字符数组存字符串  char str[] = “xxxx……”


Question: c语言学生管理信息系统

检查每一步返回值，看看操作是不是成功。 代码功能归根结底不是别人帮自己看或讲解或注释出来的；而是被自己静下心来花足够长的时间和精力亲自动手单步或设断点或对执行到某步获得的中间结果显示或写到日志文件中一步一步分析出来的。 提醒：再牛×的老师也无法代替学生自己领悟和上厕所！ 单步调试和设断点调试（VS IDE中编译连接通过以后，按F10或F11键单步执行，按Shift+F11退出当前函数；在某行按F9设断点后按F5执行停在该断点处。）是程序员必须掌握的技能之一。

顶楼上，建议改成 if(NULL==q)n=1; 这样可以杜绝少一个等号的错误

您说的建议  我会听 不过现在没那么多时间了  这个作业 马上就要交了  希望帮帮忙

这种问题就是锻炼你能力的时候，在程序加载的时候下个断点，看下读取的数据和写文件的数据是否一致，多调试，多下断点。

已经改过来了 可是我再次打开的时候之前存储的数据 显示的是乱码 不知为何 是保存那块还是存储那块的代码有问题   求指教  最好能改正过来代码发上 谢谢

add函数中：  if(q=NULL)n=1;  改成  if(q==NULL)n=1;  两个等号


Question: 全角字符与半角字符的问题

引用 6 楼 wopabe 的回复:[quote=引用 3 楼 zhao4zhong1 的回复:] 王八的屁股——规定（龟腚）！ 能说点有用的吗？[/quote] 在我看来，规定比数据更有用，数据比代码更有用。

没有关系........

引用 楼主 wopabe 的回复:我研究了全角与半角的关系 一般是这样的，全角2个字节，半角1个字节，全角的高字节为0xa3，低字节为半角+0x80  但是有个特殊的，是句号，句号的全角高字节是0xa1，低字节是0xa3，而且低字节与半角没有啥关系 这是为什么？  这个，只能查表的 ....  某些碰巧会有些规律。

王八的屁股——规定（龟腚）！

引用 1 楼 user-4 的回复:不说编码的都是耍流氓.....  #include <iostream> int main() {         std::printf("全角: %x  半角: %x  ", static_cast<int>(U'，'), static_cast<int>(U',') ); }   全角: ff0c  半角: 2c 你这是Unicode吧

引用 3 楼 zhao4zhong1 的回复:王八的屁股——规定（龟腚）！ 能说点有用的吗？


Question: 数据类型的float型

float和double的精度是由尾数的位数来决定的。浮点数在内存中是按科学计数法来存储的，其整数部分始终是一个隐含着的“1”，由于它是不变的，故不能对精度造成影响。  float：2^23 = 8388608，一共七位，这意味着最多能有7位有效数字，但绝对能保证的为6位，也即float的精度为6~7位有效数字

浮点数格式 http://bbs.csdn.net/topics/390676437

请亲自打开include\float.h一眼。

并不是说结果里显示的和你写的一样就是有效的。  它们只是碰巧一样而已。

浮点数的精度 和 printf %f 的精度不是一个概念。 你把数乘以10，你会发现第9位的。

后面的位不不一定正确，printf 用 %0.9f 还可以输出9位呢

vs试了下  改%3.7f是肯定能打出后面的，但第6位及以后都不准了。  又想起那个梗，数学家喷计算机，1/3都表示不了，还calculator

楼上说得好，不要迷信书本

计算机保存浮点数时，是把数看作a*2^b这样的形式，不是以10为底数，而是以2为底数。即将数转换成以2为底的“科学计数法”的形式，再由这个形式反转换成10进制形式，但数据又有位数限制，所以有些数转换成二进制计数法形式后位数超出了限制的位数，那么转换回来时得到的数就会与原数有偏差，不同的数这个偏差是不同的，有可能在小数点后第7位，也有可能在第8位，也有可能在其他位数，但能保证小数点第6位之前是正确的。  不要迷信书、考题、老师、回帖； 要迷信CPU、编译器、调试器、运行结果。 并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。 任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实！

%f格式控制的最多多少位，多了就进行“四舍五入”（具体怎么入跟编译器有关）  你可以%3.7试试


Question: 找了一天bug。。。OJ漏点求助

引用 4 楼 nanjun520 的回复:看了一下题目  你的肯定不符合题目要求的   1.单次运行你的可能是正确的， 只要出现上一次是错误的输入，下次即便是正确的输入你也会判断错误。 因为你的  int check[9 + 1] = {0}; 这个在 下一次循环的时候没有置空 for ( turn = 1; turn <= n; turn ++ ){ 在这里循环的时候应该 设置 check[]的值为0  2. 题目要求是 应该是所有的输入之后， 再给结果吧   你的是每次输入就出结果  哇，十分感谢！！ 第一次没注意题目要跑n次，就声明时={0}处理，后来发现草草加了个for() (都没缩进...) 然后单步时来回跑了很多数据，但都是跑完一次就中止了，没有连续单步，所以就漏掉了 >.<  另外每次输出也可以过oj的，这个可能跟oj实现有关。一般只看输出不看输入及输入输出顺序关系吧。  再次感谢！

有的oj就是这么坑

引用 1 楼 user-3 的回复:找BUG很好的方法就单步跟踪程序运行，观察程序流程及变量变化 跟踪过了，在很多例子上都符合我的预期 只是不知道漏的特殊case长什么样...

找BUG很好的方法就单步跟踪程序运行，观察程序流程及变量变化


Question: linux 中批处理时出现报错，但是已经分配过权限

或者： sudo  xx.sh

sudo  su 或者 su 再执行你的shell批处理

求回复，在线等

我一应给bak文件权限了，但是还是同样的错误

你得对bak目录有执行权限


Question: 求大神指教

引用 4 楼 qq_27091599 的回复:我用su到root下修改了权限，还是不行，报的还是这个的错。 这个是批处理程序make时遇到的问题  以root用户运行shell批处理试试。

sudo  su

写了权限，还是报这个错误

没有写权限？


Question: 如何判断一个数组里有没有  ±  这个符号

那就查找子字符串  再不让使用unicode

移位运算。。。。

strstr(a, "±");  ======================== GBK编码的话有可能得到错误结果。

||运算符应该知道吧


Question: 在函数外部定义外部变量时，省略储存类型符号，则该变量具体储存在哪个储存区域？是内存堆栈、是CPU寄存器里还是内存数据区？

理解和讨论之前请先学会如何观察！

在执行中自动分配内存，寄存器

外部变量 某些常量（包括字符串字面值） 静态变量（外部的，函数内部的，类的静态变量，无名名空间内的变量） 这些都是放在一起的，放在数据区（也就是所谓静态区，或者全局区）  编译前和编译后的程序结构是不同的 所谓作用域，只是针对源代码的 不是针对二进制的  数据存放的位置，不同于C，C++的作用域。 这是两个不同的概念。  实际上编译后，数据只是区分全局的，局部的。以及动态分配的三种形态。 而代码，对于编译后的程序，本身也是一种数据，加载程序读程序结构 安排代码，数据的存储位置，并开辟栈空间 编译后，只有函数内定义的非静态变量，和函数形参是真正的局部数据 会存放在栈区，或者寄存器中，在执行中自动分配内存 全局的数据，和代码，加载过程分配内存 全局的数据，如果可能会在加载时初始化。 部分不能在加载时候，初始化的 一部分在加载后运行时初始化 很可能在main函数执行之前初始化。 一部分（主要指函数内部静态变量），在执行的时候，初始化。

顶一下1楼，内存分为数据区， 栈，堆，代码区！ 一般常量都是放在数据区！堆是动态分配内存放的地方，栈放的是你定义的局部变量！

VMMap 是进程虚拟和物理内存分析实用工具。http://technet.microsoft.com/zh-cn/sysinternals/dd535533

放在全局区，是全局变量

确切的说，编译后，内存分为一下几个区（段）： 数据区（段），代码区（段），栈区（段），堆区（段） 不过，编译的时候，还会细分一下 其中 例如bss 段等等  静态变量，外部变量一般在数据区。 然而常量，以及字符串字面值，通常在数据区，但是也可能会放在代码区。 而函数内部非静态变量放在栈区，或者寄存器中 动态分配的内存（非变量对象,是一种无名对象）在堆区。 函数形式参数，在栈区  类，结构的成员变量，当对象定义（或者用new 创建）的时候，才会分配内存(在对象内部) 由于编译优化的存在 很多常量，部分引用，部分变量，会被优化掉（不占用内存空间） 很多人称呼，数据区为静态区，因为（外部，或者静态）变量是一次定义，终生（直到程序结束）使用的。


Question: 链表选择排序

仅供参考：//不带表头结点的单向链表 #include <stdio.h> #include <stdlib.h> #include <malloc.h> #include <time.h> #include <locale.h> struct NODE {     int          data;     struct NODE *next; } *head,*p,*q,*s,*p1,*p2,*q1,**ta; int i,k,n,t,m,v,N=10; int main() {     setlocale(LC_ALL,"chs");     srand(time(NULL));      head=NULL;      printf("创建%d个节点的单链表:",N);//创建N个节点的单链表     p=head;     for (i=0;i<N;i++) {         q=(struct NODE *)malloc(sizeof(struct NODE));         if (NULL==q) exit(1);         q->data=rand()%100;//填写0..99的随机值         q->next=NULL;         if (NULL==p) {             head=q;             p=head;         } else {             p->next=q;             p=q;         }     }      //输出整个单链表     s=head;     while (1) {         if (NULL==s) {             printf("\n");             break;         }         printf("%02d->",s->data);         s=s->next;     }      k=3;     v=5;     printf("将值为%d的结点插入到单链表的第%d个结点前:",v,k);//将值为v的结点插入到单链表的第k个结点前     n=0;     p=head;     while (1) {         if (NULL==p) {             break;         }         n++;         if (k==1) {             q=(struct NODE *)malloc(sizeof(struct NODE));             if (NULL==q) exit(1);             q->data=v;             q->next=head;             head=q;             break;         } else {             if (k-1==n) {                 q=(struct NODE *)malloc(sizeof(struct NODE));                 if (NULL==q) exit(1);                 q->data=v;                 q->next=p->next;                 p->next=q;                 break;             }         }         p=p->next;     }      //输出整个单链表     s=head;     while (1) {         if (NULL==s) {             printf("\n");             break;         }         printf("%02d->",s->data);         s=s->next;     }      k=5;     printf("删除第%d个节点:",k);//删除第k个节点     n=0;     p=head;     while (1) {         if (NULL==p) {             break;         }         n++;         if (k==1) {             q=head;             head=head->next;             free(q);             break;         } else {             if (k-1==n) {                 q=p->next;                 if (q) {                     p->next=q->next;                     free(q);                 }                 break;             }         }         p=p->next;     }      //输出整个单链表     s=head;     while (1) {         if (NULL==s) {             printf("\n");             break;         }         printf("%02d->",s->data);         s=s->next;     }      printf("从小到大排序:");//从小到大排序     for (p=head,p1=NULL;p!=NULL;p1=p,p=p->next) {         for (q=p->next,q1=p;q!=NULL;q1=q,q=q->next) {             if (p->data > q->data) {                  //交换data //              printf("swap %02d %02d\n",p->data,q->data); //              t=p->data;p->data=q->data;q->data=t;                  //或者                  //交换next //              printf("swap %02d %02d\n",p->data,q->data);                 if (p==head) {//p是头                     if (p->next==q) {//pq挨着                         head=q;                         p->next=q->next;                         q->next=p;                         q=p;                         p=head;                     } else {//pq不挨着                         head=q;                         p2=p->next;                         p->next=q->next;                         q->next=p2;                         q1->next=p;                         q=p;                         p=head;                     }                 } else {//p不是头                     if (p->next==q) {//pq挨着                         p1->next=q;                         p->next=q->next;                         q->next=p;                         q=p;                         p=p1->next;                     } else {//pq不挨着                         p1->next=q;                         p2=p->next;                         p->next=q->next;                         q->next=p2;                         q1->next=p;                         q=p;                         p=p1->next;                     }                 }                  //输出整个单链表 //              s=head; //              while (1) { //                  if (NULL==s) { //                      printf("\n"); //                      break; //                  } //                  printf("%02d->",s->data); //                  s=s->next; //              } //              getchar();             }         }     }      //输出整个单链表并计算链表长度n     n=0;     s=head;     while (1) {         if (NULL==s) {             printf("\n");             break;         }         printf("%02d->",s->data);         n++;         s=s->next;     }      printf("将整个链表逆序:");//将整个链表逆序     if (n>=2) {         p=head;         q=p->next;         p->next=NULL;         while (1) {             q1=q->next;             q->next=p;             p=q;             q=q1;             if (NULL==q) break;         }         head=p;     }      //输出整个单链表     s=head;     while (1) {         if (NULL==s) {             printf("\n");             break;         }         printf("%02d->",s->data);         s=s->next;     }      m=4;     n=6;     printf("将单链表中前%d个结点和后%d个结点进行互换:",m,n);//将单链表中前m个结点和后n个结点进行互换,m+n为链表总长     k=0;     p=head;     while (1) {         if (NULL==p) {             break;         }         k++;         if (m==k) {             q=p;         }         s=p;         p=p->next;     }     q1=head;     head=q->next;     s->next=q1;     q->next=NULL;      //输出整个单链表     s=head;     while (1) {         if (NULL==s) {             printf("\n");             break;         }         printf("%02d->",s->data);         s=s->next;     }      //释放所有节点     p=head;     while (1) {         if (NULL==p) {             break;         }         q=p->next;         free(p);         p=q;     }      return 0; } //创建10个节点的单链表:08->74->07->23->03->99->31->56->88->16-> //将值为5的结点插入到单链表的第3个结点前:08->74->05->07->23->03->99->31->56->88->16-> //删除第5个节点:08->74->05->07->03->99->31->56->88->16-> //从小到大排序:03->05->07->08->16->31->56->74->88->99-> //将整个链表逆序:99->88->74->56->31->16->08->07->05->03-> //将单链表中前4个结点和后6个结点进行互换:31->16->08->07->05->03->99->88->74->56-> //

char tsex[2]; 你光开2个字节是不够的（中文一般是占2~3个字节，看你的编码了），而且还有'\0' 你改成char tsex[4];试试

可是为什么在显示输出时，性别那一项输出不对，比如我输入性别是男，场馆是海洋馆，结果输出的性别就是，男海洋馆，场馆输出还是海洋馆正常。

引用 2楼我是你的主体 的回复:void output(TMR*p) { 	TMR *head=p; 	if(p==0) 	printf("null\n"); 	else 	while(p!=0) 	{ 		printf("姓名：%s;\t编号：%d;\t性别：%s;\t年龄：%d;\t场馆：%s;\t表演时间：%s;\t节目名称：%s;\t宠物编号：%d;\n",  		p->tname,p->tno,p->tsex,p->tage,p->tmuseum,p->tshowtime,p->tshowname,p->ano);  	p=p->next; 	} 	p=head; } 这样再声明一个函数可以吗？ 我排序的是编号，可以输出所有的信息么？如果你排序没错的话，这样输出是可以的

方法一：排序后，将你要看的输出到控制台，可以使用printf  方法二：单步跟踪程序运行，每一步可以观察各变量的值


Question: C 语言EOF问题

不要把 fopen("...","...");fscanf,fprintf,fgets,fgetc,fclose //读时把\r\n替换成\n，写时把\n替换成\r\n;读到\x1a就设置EOF;读写的内容当字符看待 和 fopen("...","...b");fseek,ftell,fread,fwrite,fgetc,fclose //不作以上替换，遇到\x1a仍继续读;读写的内容当字节看待 弄混了

其实EOF并不是Console的结尾。  MS-DOS编辑器里里面Ctrl+Z才是结尾的意思。   我正好在看C primer plus第六版， 来自13.5.2 二进制模式和文本模式

这应该是跟具体的系统以及编译器有关，要看Windows和Ubuntu下面的C库中getchar函数是如何实现的

这么说吧。你可以认为这个是微软的认为文件结束的一个行为。回车+ctrl z +回车

换行也算一个字符

#include <stdio.h> int main(int argc, char const *argv[]) {    	int i; 	int ch; 	for (i = 0; (ch  = getchar()) != EOF && ch != 26; i++){} 	printf("%d\n", i);  	return 0; }

你可以把这一句getchar() != EOF换成getchar() != '\n',判断只要回车就结束就好了   /* 其实就是一个看输入了多少个字符的小程序 */ #include <stdio.h> int main(int argc, char const *argv[]) {	 	int i; 	for (i = 0; getchar() != '\n'; i++){} 	printf("%d\n", i);  	return 0; }


Question: 有可以在MCU上运行的数据压缩/解压缩源码吗？

引用 6 楼 jiqiang01234 的回复:也许可以多使用一些“位域”再压缩一下。 最好能把数据包格式发一下，看看还有什么地方可以压缩的。  另：一次不到1k数据的压缩，没有必要，因为压缩以后可能比原始数据还大。  用位域来表示我们也有考虑过的 项目是让终端一个装置收集一些设备的状态信息（这些状态信息也可以用0/1来表示，就是是否故障），然后上送给服务器，但是如果用位域来表示的话，可能数据量还会更大一些，因为可能的故障会有280多种，如此一来，每次上送数据就必须使用超过32字节表示，而实际上设备大多数情况下是没有处于故障状态的。现在我是使用2字节表示这个状态，每个状态编个号，取值范围在0~32767之间，两个字节中第二字节的最高位表示是否故障，其它15位表示故障编号。故障状态发生变化时才上送信息。如此一来，数据量会少很多了。

现在流行用空间换取时间，不怎么会碰到用时间换取空间的情况。

引用 4 楼 hemowolf 的回复:[quote=引用 1 楼 jiqiang01234 的回复:] 得看你是什么性能的mcu，c51什么的就不用考虑了。如果能到ARM9以上，可以用。 另：1000字节很多吗？需要这么节省？  可以肯定不是ARM9，在ARM9上可以跑java了，我还操这份心干嘛  现在通信协议是我在制订，我做的是服务端的开发，MCU上的东西我也不懂 公司一直强调流量、流量、流~量~，成本、成本、成~本~ 经过估算，一个MCU接多台终端，一个终端一个月估计有10M流量，我也觉得很省了，但人家还想要省  现在协议都没有用字符串，全部是二进制数据，能省的都省了，还想省的话，就只能压缩数据传输了 [/quote] 也许可以多使用一些“位域”再压缩一下。 最好能把数据包格式发一下，看看还有什么地方可以压缩的。  另：一次不到1k数据的压缩，没有必要，因为压缩以后可能比原始数据还大。

51类也有高速的，还有51的升级版16Bits的251 类芯片 intel 也有16Bits的MCU

dsp 做离散余弦变换，是很平常的 MCU类型很多，有低速的，有高速的 有各种专用的芯片 看你做什么事情，选用那种芯片了

ARM 很便宜啊，十几块的都有 为啥不用呢

引用 1 楼 jiqiang01234 的回复:得看你是什么性能的mcu，c51什么的就不用考虑了。如果能到ARM9以上，可以用。 另：1000字节很多吗？需要这么节省？  可以肯定不是ARM9，在ARM9上可以跑java了，我还操这份心干嘛  现在通信协议是我在制订，我做的是服务端的开发，MCU上的东西我也不懂 公司一直强调流量、流量、流~量~，成本、成本、成~本~ 经过估算，一个MCU接多台终端，一个终端一个月估计有10M流量，我也觉得很省了，但人家还想要省  现在协议都没有用字符串，全部是二进制数据，能省的都省了，还想省的话，就只能压缩数据传输了


Question: 各位大神来解题啊，刚开始有1头母牛，4岁后每年生育1头母牛，15岁停止生育，20岁死亡，问70年后牛群有多少牛?

引用 10 楼 ForestDB 的回复:首先，我用excel手动模拟了一下计算过程，行首是年纪，列首是年份，由此得出计算过程： 1、对每一行针对上一行长一岁； 2、4岁到14岁的牛生1岁牛（求和）；      1   2   3   4   5   6   7   8   9   10  11  12  13  14  15  16  17  18  19  sum 1   1                                                                           1 2       1                                                                       1 3           1                                                                   1 4   1           1                                                               2 5   1   1           1                                                           3 6   1   1   1           1                                                       4 7   2   1   1   1           1                                                   6 8   3   2   1   1   1           1                                               9 9   4   3   2   1   1   1           1                                           13 10  6   4   3   2   1   1   1           1                                       19 11  9   6   4   3   2   1   1   1           1                                   28 12  13  9   6   4   3   2   1   1   1           1                               41 13  19  13  9   6   4   3   2   1   1   1           1                           60 14  28  19  13  9   6   4   3   2   1   1   1           1                       88 15  40  28  19  13  9   6   4   3   2   1   1   1           1                   128 16  59  40  28  19  13  9   6   4   3   2   1   1   1           1               187 17  87  59  40  28  19  13  9   6   4   3   2   1   1   1           1           274 18  126 87  59  40  28  19  13  9   6   4   3   2   1   1   1           1       400 19  184 126 87  59  40  28  19  13  9   6   4   3   2   1   1   1           1   584 20  270 184 126 87  59  40  28  19  13  9   6   4   3   2   1   1   1           853 21  394 270 184 126 87  59  40  28  19  13  9   6   4   3   2   1   1   1       1247 22  575 394 270 184 126 87  59  40  28  19  13  9   6   4   3   2   1   1   1   1822  由算法写出代码：  # include <stdio.h> # include <string.h>  long sum(long array[], int a, int b) {     long result = 0;     for (int i = a; i <= b; i++)         result += array[i];     return result; }  int main() {     long age[20] = { 0, 1, 0, }; // 为清晰，不使用age[0]；age[1]表示1岁，类推      for (int year = 2; year <= 70; year++) // 从第2年开始     {         memmove(&age[2], &age[1], 18 * sizeof(long)); // 1到18岁变成了2到19岁         age[1] = sum(age, 4, 14); // 4到14岁的牛生1岁牛         printf("year %d: %ld\n", year, sum(age, 1, 19)); // 牛20岁死亡     }      return 0; }   year 2: 1 year 3: 1 year 4: 2 year 5: 3 year 6: 4 year 7: 6 year 8: 9 year 9: 13 year 10: 19 year 11: 28 year 12: 41 year 13: 60 year 14: 88 year 15: 128 year 16: 187 year 17: 274 year 18: 400 year 19: 584 year 20: 853 year 21: 1247 year 22: 1822 year 23: 2662 year 24: 3890 year 25: 5684 year 26: 8305 year 27: 12135 year 28: 17731 year 29: 25908 year 30: 37856 year 31: 55313 year 32: 80821 year 33: 118093 year 34: 172553 year 35: 252127 year 36: 368398 year 37: 538289 year 38: 786526 year 39: 1149240 year 40: 1679224 year 41: 2453615 year 42: 3585124 year 43: 5238440 year 44: 7654199 year 45: 11184010 year 46: 16341629 year 47: 23877735 year 48: 34889192 year 49: 50978694 year 50: 74488031 year 51: 108838934 year 52: 159031102 year 53: 232369893 year 54: 339529603 year 55: 496107090 year 56: 724891859 year 57: 1059183022 year 58: 1547635913 year 59: 2261343762 year 60: 3304185155 year 61: 4827943333 year 62: 7054397903 year 63: 10307604364 year 64: 15061059666 year 65: 22006618635 year 66: 32155191897 year 67: 46983881670 year 68: 68650970702 year 69: 100310055509 year 70: 146569045320   补充说明： 1、这基本上是Fibonacci的变体：F(n) = F(n - a) + F(n - b) - F(n - c) - F(n - d) 根据题目中1头，4岁/15岁/20岁等几个条件，可以推出上式当中a/b/c/d的具体值； 2、算法基本是不变的，变的是对条件的理解（括号中是我代码中的理解）： 刚开始有1头母牛：这是第1年还是第0年？这头牛是1岁还是0岁？（第1年，1岁） 4岁后每年生育1头母牛：是4岁就生还是5岁生？生的小牛是1岁还是0岁？（4岁生1岁牛） 15岁停止生育：哪一岁还能生？14还是15？（14岁生，15岁就不生了） 20岁死亡：20岁活着还是死了？（19岁活着，20岁就死了）   大神！！

“多一少一”问题占程序员常犯错误的10%以上！ 避免“多一少一”问题的方法之一是将比如<10甚至<5的数代入程序片断，掰手指头心算验证一下程序到底应该写为 x、x-1、x+1中的哪个？ <、<=、==、>、>=中的哪个？

引用 2 楼 yyxgs 的回复:上面不对，是4年后每年都生，看错了！     如果第4年是2头的话，第7年应该是6头吧

年数y,当年头数a[y] 当年保有数量 num=a[y]; 当年育龄数  canborn =y<4?0 : a[y-4];// 4年前的头数 当年不可生育数 cannnotborn =y<15?0:a[y-15];//15年前头数 当年死亡数 dead=y<20:0?a[y-20];//20年前头数 当年出生数 born = canborn -cannotborn;//到达育龄数减去绝育数 当年保有数 a[y] =a[y-1] +born -dead; 去年头数，加上新生数，减去死亡数 4,15,20 如果需要斟酌，可以修改成 3，14，19

引用 7 楼 nanjun520 的回复: 我算了一下  好像 int 溢出了   http://www.oschina.net/question/193385_29597  哈哈，那就用双长

建一个长度为20的数组，分别存储0~19岁的活牛，每过1年4~14岁牛的总数为新生牛数  #include<stdio.h>  int main() { 	int herd[20] = {1,0,0};			//牛群 	int total = 1;					//总牛数 	int born;						//新生牛 	int i,j;  	for(i=1;i<=70;i++){ 		//显示当前各年龄的牛数 		printf("第%02d年: %d头",i,total); 		for(j=0;j<20;j++) 			printf("%c%d",j?'\t':'\n',herd[j]); 		printf("\n");  		total = born = 0; 		for(j=19;j>=0;j--){ 			total += herd[j];		//0~19岁牛仍在，包含在总数中 			if(j>=4 && j < 15) 				born += herd[j];	//4~14岁牛生小牛 			herd[j+1] = herd[j];	//每头牛均长1岁 		} 		herd[0] = born; 		total += born; 	} 	return 0; }   这里题目存在很多岐义： 刚开始有1头母牛，开始的一头牛几岁？0/1 4岁后每年生育1头母牛，从几岁开始生？4/5 15岁停止生育，生到几岁？14/15 20岁死亡活到多少岁？19/20

写了一个效率也不高，不过算的结果是2628607715，不保证正确

上面不对，是4年后每年都生，看错了！


Question: C语言代码错误  不能正常运行 求大神们指正

电脑内存或文件内容或传输内容只是一个一维二进制字节数组及其对应的二进制地址； 人脑才将电脑内存或文件内容或传输内容中的这个一维二进制字节数组及其对应的二进制地址的某些部分看成是整数、有符号数/无符号数、浮点数、复数、英文字母、阿拉伯数字、中文/韩文/法文……字符/字符串、汇编指令、函数、函数参数、堆、栈、数组、指针、数组指针、指针数组、数组的数组、指针的指针、二维数组、字符点阵、字符笔画的坐标、黑白二值图片、灰度图片、彩色图片、录音、视频、指纹信息、身份证信息……  推荐使用WinHex软件查看硬盘或文件或内存中的原始字节内容。

fread和fwrite是二进制读取和写入，所以你看到的是乱码

修正之后 #include<stdio.h> #include<stdlib.h> #include<string.h> typedef struct student//定义学生 {    char name[10];    long long number;    char sex[2];    int math;    int english;    int cyuyan;    int sum;    }student;    typedef struct unit//定义接点    {    	student date;    	struct unit *next; 	} unit; 	unit* build()//建立链表并返回指针 	{ 	unit *p; 	if((p=(unit*)malloc(sizeof(unit)))==NULL){ 		printf("=>初始化失败！"); 		return 0;	 	} 	else{ 		p->next=NULL; 		p->date.number=0; 		printf("初始化成功！\n"); 		return p;	 	} 	}     void add(unit *head)//增加学生  { 	unit *p,*q; 	int m,n=0; 	q=head->next; 	p=(unit*)malloc(sizeof(unit)); 	printf("=>请输入新生姓名\n"); 	gets(p->date.name);fflush(stdin); 	printf("=>请输入学生学号！\n"); 	while(n==0){ 		scanf("%d",&m);fflush(stdin); 	   	if(q==NULL)n=1; 		while(q){ 			if(q->date.number==m){ 				printf("=>你输入的学号与已有学生的学号相同，请重新输入！\n"); 				q=head->next; 				break;	  			} 			else{ 				q=q->next; 				if(q=NULL)n=1; 			} 	} 	 p->date.number=m;} printf("=>请输入性别！\n"); gets(p->date.sex);fflush(stdin); printf("=>请输入数学成绩\n"); scanf("%d",&m);fflush(stdin); p->date.math=m; printf("=>请输入英语成绩\n"); scanf("%d",&m);fflush(stdin); p->date.english=m; printf("=>请输入c语言成绩\n"); scanf("%d",&m);fflush(stdin); p->date.cyuyan=m; p->date.sum=(p->date.math+p->date.english+p->date.cyuyan); q=head->next; head->next=p; p->next=q; head->date.number++; }  void deletion(unit *head)//删除一名学生 { 	unit *p=head->next,*q=head; 	char N[10]; 	printf("=>请输入你想要删除的学生姓名！\n"); 	gets(N);fflush(stdin); 	if(p==NULL)  	printf("=>系统无学生可删除！\n"); 	while(p){ 		if(strcmp(p->date.name,N)==0){ 			q->next=p->next; 			head->date.number--; 			printf("=>删除%s成功！\n",p->date.name); 			free(p); 			break;	 		} 		else{ 			p=p->next; 			q=q->next; 			if(p==NULL) 			printf("=>你要删除的学生不存在，删除失败！\n"); 			 		}	 	}	  }    int display(unit *head)//显示学生信息 统计汇总   {  	unit *p=head->next;  	int m,n=0;  	char N[10];  	if(p==NULL){  		printf("=>系统无学生！\n");  		return 0;  	 } 	 while(n==0){ 	 	printf("-------------------------\n"); 	 	printf("=>请选择你的操作！\n"); 		printf("=>\t\t\t1.显示所有\t2.按姓名查找\n\t\t\t3.按学号查找\t4.返回主菜单\n"); 	 	scanf("%d",&m); 		fflush(stdin); 		switch(m) 		{ 		case 1:printf("\n=>该系统拥有%d名学生！\n\n",head->date.number); 		p=head->next; 		printf("\t姓名\t学号\t性别\t数学\t英语\tc语言\t综合\n\n"); 		while(p){ 			printf("\t%s\t%d\t%s\t%d\t%d\t%d\t%d\n",p->date.name,p->date.number,p->date.sex,p->date.math,p->date.english,p->date.cyuyan,p->date.sum); 			p=p->next;	 		} 		break;	 		case 2:printf("=>请输入查找姓名！\n"); 		gets(N);	 		fflush(stdin); 		p=head->next;  		while(p){ 			if(strcmp(p->date.name,N)==0){ 				printf("\t姓名\t学号\t性别\t数学\t英语\tc语言\t综合\n"); 				printf("\t%s\t%d\t%s\t%d\t%d\t%d\t%d\n",p->date.name,p->date.number,p->date.sex,p->date.math,p->date.english, 				p->date.cyuyan,p->date.sum); 				break; 	 			}else{ 			p=p->next; 			if(p==NULL) 			printf("=>你要查的学生不存在！\n");	 				 			} 		}break; 		case 3:printf("=>请输入查找学号！\n"); 		scanf("%d",&m); 		fflush(stdin); 		p=head->next; 		while(p){ 			if(p->date.number==m){ 			printf("\t姓名\t学号\t性别\t数学\t英语\tc语言\t综合\n");	 				printf("\t%s\t%d\t%s\t%d\t%d\t%d\t%d\n",p->date.name,p->date.number,p->date.sex,p->date.math,p->date.english, 				p->date.cyuyan,p->date.sum); 				break; 			}else{ 				p=p->next; 			if(p==NULL) 			printf("=>你要查的学生不存在！\n");		 			}		 		}	 		break; 		 case 4:n=1;break; 			}	 	 } 	 return 1;   }    int range(unit *head)//排序   {   	unit *p=head,*q=head->next;   	int n,i,m=head->date.number;   	printf("--------------------------\n");   	printf("\t\t\t1.按学号\t2.按成绩\n\n");   	printf("=>请选择操作！\n");	   	scanf("%d",&n); 	fflush(stdin) ; 	if(q==NULL){ 			printf("=>无学生可排序！\n"); 			return 0;		 	}  	switch(n){ 		case 1:for(i=0;i<=m;i++){ 			p=head;q=head->next; 			while(q->next){ 				if((q->date.number)>(q->next->date.number)){ 					p->next=q->next; 					p=p->next; 					q->next=p->next; 					p->next=q;	 				}else{ 					p=p->next;q=q->next;	 				} 			} 		}	break; 		}	 		printf("=>排序成功！\n"); 		return 1;   }   void menu(){   	printf("-------------主菜单------------------\n");   	printf("\t1.新建系统\t2.添加学生\n\t3.删除学生\t4.查看信息\n\t5.排列顺序\t6.读取信息\n\t7.保存信息\t8.退出系统\n");   	printf("-------------------------------------\n");     printf("=>请输入你的操作！\n");   } int save(unit *head)//保存   {   	FILE *fp; 	  unit *p; 	  p=head; 	  if((fp=fopen("xuesheng.txt","wb+"))==NULL) 	  { 	  printf("=>保存失败！\n");	 	  	return 0; 	 	  }   	while(p){   		fwrite(&(p->date),sizeof(student),1,fp);   		p=p->next; 	  }   	printf("=>保存成功！\n");   	fclose(fp);   	return 1;    }      unit* read()//读入系统学生信息    {    	int i;    	unit *p,*q,*head;    	FILE *fp;    	if((head=(unit*)malloc(sizeof(unit)))==NULL){    		printf("=>开发空间失败！\n");    		exit(1);  	   }    	else{    		head->next=NULL;    		head->date.number=0;//头结点存放学生人数; 	   }    	q=head;    	if((fp=fopen("xuesheng.txt","ra"))==NULL){    		printf("=>系统无学生！\n");    		return 0; 	 	   }    	fread(&(q->date),sizeof(student),1,fp);    	for(i=0;i<head->date.number;i++){    		if((p=(unit*)malloc(sizeof(unit)))==NULL)    		{    		printf("=>读值失败！\n");    		exit(1); 		   }    			fread(&(q->date),sizeof(student),1,fp);    		p->next=q->next;    		q->next=p; 	   }    		fclose(fp); 	return head; 	}  int main(){ 	int m=0,n,i; 	unit *head; 	printf("--------------------------------------------------------------------------\n"); 	printf("\t\t\t* 欢迎使用学生成绩管理系统 *！\n"); 	printf("\n\t\t\t\t\t\t\t\t\t\t\t\t\t编译学生：王亚辉\n\n"); 	printf("正在读取系统信息。。。。。。\n"); 	if((head=read())==0){ 		printf("=>是否新建?！\n"); 		printf("\t\t\t是(1)\t\t否(2)\n"); 		scanf("%d",&i); 		fflush(stdin); 		if(i==1) 		head=build();	 		else if(i==2) 			exit(1); 		} 		else 			printf("=>信息导入成功！\n");  	 	while(m==0){ 				menu(); 				scanf("%d",&n); 				fflush(stdin); 				switch(n){ 					case 1:head=build();break; 					case 2:add(head);break; 					case 3:deletion(head);break; 					case 4:display(head);break; 					case 5:range(head);break; 					case 6:head=read();if(head!=0) printf("=>读取存盘成功！\n");break; 					case 7:save(head);break; 					case 8:m=1;break; 	 				} 				 			}		 		}   但是每次存盘还正常 但是再次打开这个程序显示的时候显示的就是乱码了 是不是保存或者读取那块出问题了 求大神教我 帮我指正

关于自己是否适合编程的很简单的测试： 在报纸或杂志上随便找一段约1000字的文章，在Word中输入一遍。输完后再参考下面答案：  A里面有10处以上文字或标点错误 B里面没有文字或标点错误并敢为此跟人打赌 C里面没有文字或标点错误并且字体和排版完全与原稿一致 D打印在半透明的纸上和原稿重叠在一起检查一模一样，且自我感觉很有成就感  A不适合编程（理由：打字准确度偏低、粗心大意） B初级程序员（理由：打字准确度很高、认真细致、自信、理解全角半角概念） C高级程序员（理由：在B的基础上理解字体和排版也是电脑打印的重要因素、但相比D还不够偏执、精益求精、结果可验证） D软件项目经理（理由：能针对项目给出令人信服的细致到极点的需求说明和典型测试用例。用户几乎挑不出毛病。专业！）  如果想从A变成B的话，到我的资源http://download.csdn.net/detail/zhao4zhong1/4084259里面下载“适合程序员的键盘练习”

机器人有时比真人更擅长解决因机器不听人话导致的各种问题。

保存失败是因为if((fp=fopen("d:\\学生","wb+"))==NULL); 其实并不是创建文件失败了，把后面的分号去掉，修改为if((fp=fopen("d:\\学生","wb+"))==NULL) 还有 printf("=>请输入数学成绩\n"); 下面的 scanf("%d,&m");//双引号位置不正确 拼写错误，修改为scanf("%d“,&m);

引用 6 楼 qq423399099 的回复:代码功能归根结底不是别人帮自己看或讲解或注释出来的；而是被自己静下心来花足够长的时间和精力亲自动手单步或设断点或对执行到某步获得的中间结果显示或写到日志文件中一步一步分析出来的。 提醒：再牛×的老师也无法代替学生自己领悟和上厕所！ 单步调试和设断点调试（VS IDE中编译连接通过以后，按F10或F11键单步执行，按Shift+F11退出当前函数；在某行按F9设断点后按F5执行停在该断点处。）是程序员必须掌握的技能之一。  你这怎么和别的帖子里的回答一样……还不是你发的，你也是机器人？

谢谢  对了 save这块儿总是保存失败 怎么办？

if((head=(unit*)malloc(sizeof(unit)))=NULL){  后面那个 = 改成 ==   判断是否相等用的是==

不一样  谢谢您 解决了我的问题

修改后的和修改前的怎么一样？

lilianjie001 谢谢您帮我解决了问题

if((head=(unit*)malloc(sizeof(unit)))=NULL) 错误 这样等于head=(unit*)malloc(sizeof(unit));head=NULL 修改为if((head=(unit*)malloc(sizeof(unit)))==NULL)即可


Question: c语言 声明与定义参数不统一？

引用 7 楼 bigPillow的回复:[quote=引用 5 楼 paschen 的回复:] 可以编译不代表那种声明是对的  另参看： http://en.cppreference.com/w/cpp/language/declarations  谢谢版主的回复， 我疑问的是，为什么声明的时候缺少一个参数就可以，缺少两个以上就不可以。[/quote] 缺少两个也不可以，可以编译不代表正确，也不代表实现和声明对上了

引用 15 楼 fefe82 的回复:[quote=引用 11 楼 woshidahuaidan2011 的回复:] [quote=引用 10 楼 fefe82 的回复:] 给完整程序。  /*test.c*/ #include"test.h" int main() {     int i=5;     func(i,i);     return 0; }  int func(int a, int b, int c) {     return (a - b - c) }   /*test.h*/ extern int func(int a, int b);/*Hey! It compiles!*/ //extern int func(int a);/*sorry , the code won't compile. */ /*this is  why??*/   ths.[/quote]  gcc5.4 无法编译通过。   $ gcc test.c test.c:10:5: error: conflicting types for ‘func’  int func(int a, int b, int c)      ^ In file included from test.c:2:0: test.h:2:12: note: previous declaration of ‘func’ was here  extern int func(int a, int b);/*Hey! It compiles!*/ [/quote]  好的,gcc编译的时候是我的失误，是不可以通过的，详细的声明在19楼。 感谢你的回答。

引用 18 楼 fefe82 的回复:[quote=引用 17 楼 woshidahuaidan2011 的回复:] [quote=引用 15 楼 fefe82 的回复:] [quote=引用 11 楼 woshidahuaidan2011 的回复:] [quote=引用 10 楼 fefe82 的回复:] 给完整程序。  /*test.c*/ #include"test.h" int main() {     int i=5;     func(i,i);     return 0; }  int func(int a, int b, int c) {     return (a - b - c) }   /*test.h*/ extern int func(int a, int b);/*Hey! It compiles!*/ //extern int func(int a);/*sorry , the code won't compile. */ /*this is  why??*/   ths.[/quote]  gcc5.4 无法编译通过。   $ gcc test.c test.c:10:5: error: conflicting types for ‘func’  int func(int a, int b, int c)      ^ In file included from test.c:2:0: test.h:2:12: note: previous declaration of ‘func’ was here  extern int func(int a, int b);/*Hey! It compiles!*/ [/quote]   gcc test.c ? 有加什么参数么？ [/quote]  没加[/quote]  好，那我明白了，昨天操作失误，声明的时候参数，给错，我的失误，现在是vs2013可以编译通过。gcc无法编译通过了。  假对函数不尽性声明，代码如下：  int main() {     int i=5;     func(i,i);     return 0; }   int func(int a, int b, int c) {     return (a - b - c); }  上面的代码可以运行，一旦声明了就不可以运行。假如不声明直接调用少一个实参，最后一个参数会是堆中的一个随机数，这是因为c语言不支持重载，编译器没有检测参数类型。  好的谢谢各位，结贴了。

引用 17 楼 woshidahuaidan2011 的回复:[quote=引用 15 楼 fefe82 的回复:] [quote=引用 11 楼 woshidahuaidan2011 的回复:] [quote=引用 10 楼 fefe82 的回复:] 给完整程序。  /*test.c*/ #include"test.h" int main() {     int i=5;     func(i,i);     return 0; }  int func(int a, int b, int c) {     return (a - b - c) }   /*test.h*/ extern int func(int a, int b);/*Hey! It compiles!*/ //extern int func(int a);/*sorry , the code won't compile. */ /*this is  why??*/   ths.[/quote]  gcc5.4 无法编译通过。   $ gcc test.c test.c:10:5: error: conflicting types for ‘func’  int func(int a, int b, int c)      ^ In file included from test.c:2:0: test.h:2:12: note: previous declaration of ‘func’ was here  extern int func(int a, int b);/*Hey! It compiles!*/ [/quote]   gcc test.c ? 有加什么参数么？ [/quote]  没加

引用 15 楼 fefe82 的回复:[quote=引用 11 楼 woshidahuaidan2011 的回复:] [quote=引用 10 楼 fefe82 的回复:] 给完整程序。  /*test.c*/ #include"test.h" int main() {     int i=5;     func(i,i);     return 0; }  int func(int a, int b, int c) {     return (a - b - c) }   /*test.h*/ extern int func(int a, int b);/*Hey! It compiles!*/ //extern int func(int a);/*sorry , the code won't compile. */ /*this is  why??*/   ths.[/quote]  gcc5.4 无法编译通过。   $ gcc test.c test.c:10:5: error: conflicting types for ‘func’  int func(int a, int b, int c)      ^ In file included from test.c:2:0: test.h:2:12: note: previous declaration of ‘func’ was here  extern int func(int a, int b);/*Hey! It compiles!*/ [/quote]   gcc test.c ? 有加什么参数么？

vs2013也至少有警告。

引用 11 楼 woshidahuaidan2011 的回复:[quote=引用 10 楼 fefe82 的回复:] 给完整程序。  /*test.c*/ #include"test.h" int main() {     int i=5;     func(i,i);     return 0; }  int func(int a, int b, int c) {     return (a - b - c) }   /*test.h*/ extern int func(int a, int b);/*Hey! It compiles!*/ //extern int func(int a);/*sorry , the code won't compile. */ /*this is  why??*/   ths.[/quote]  gcc5.4 无法编译通过。   $ gcc test.c test.c:10:5: error: conflicting types for ‘func’  int func(int a, int b, int c)      ^ In file included from test.c:2:0: test.h:2:12: note: previous declaration of ‘func’ was here  extern int func(int a, int b);/*Hey! It compiles!*/

我用gcc 4.8.5没有编译通过

引用 12 楼 pengzhixi 的回复:什么编译器？ 你好， 不管是在windows下的vs2013还是在linux在的gcc4.8现象都一样。

什么编译器？

引用 10 楼 fefe82 的回复:给完整程序。  /*test.c*/ #include"test.h" int main() {     int i=5;     func(i,i);     return 0; }  int func(int a, int b, int c) {     return (a - b - c) }   /*test.h*/ extern int func(int a, int b);/*Hey! It compiles!*/ //extern int func(int a);/*sorry , the code won't compile. */ /*this is  why??*/   ths.

给完整程序。

引用 8 楼 paschen 的回复:[quote=引用 7 楼 bigPillow的回复:][quote=引用 5 楼 paschen 的回复:] 可以编译不代表那种声明是对的  另参看： http://en.cppreference.com/w/cpp/language/declarations  谢谢版主的回复， 我疑问的是，为什么声明的时候缺少一个参数就可以，缺少两个以上就不可以。[/quote] 缺少两个也不可以，可以编译不代表正确，也不代表实现和声明对上了[/quote]、  版主好，  我知道这样调用函数是不对的，因为当调用的实参少一个参数的时候，最后一个参数会在栈按顺序找个随机值。但是使我困惑，为什么少一个参数就可以通过编译，少两个及其两个以上就不可以通过编译，这是什么原因。

引用 5 楼 paschen 的回复:可以编译不代表那种声明是对的  另参看： http://en.cppreference.com/w/cpp/language/declarations  谢谢版主的回复， 我疑问的是，为什么声明的时候缺少一个参数就可以，缺少两个以上就不可以。

引用 4 楼 zhao4zhong1 的回复:提醒：gcc是开源的。 你好 赵老师， 就算使用vs2013也是可以编译通过的，而且一旦在声明的的时候故意少用一个参数，那么在调用的时候，实参就可以少加一个参数来调用该函数。

提醒：gcc是开源的。

引用 1 楼 zhao4zhong1 的回复:C只管函数名，不管函数参数和返回值。 赵老师 您好，  我知道c++由于支持重载在编译的时候要检测形参，c语言不支持重载所以不用检测参数。  但是问题是，当声明的时候的形参少于两个的时候，编译就无法通过，所以他还是检测的。 这该怎么理解？

dll 导出函数名的那些事 关键字：　VC++ 　DLL  导出函数　 经常使用VC6的Dependency查看DLL导出函数的名字，会发现有DLL导出函数的名字有时大不相同，导致不同的原因大多是和编译DLL时候指定DLL导出函数的界定符有关系。 VC++支持两种语言：即C/C++，这也是造成DLL导出函数差异的根源 我们用VS2008新建个DLL工程，工程名为"TestDLL" 把默认的源文件后缀 .CPP改为.C（C文件） 输入测试代码如下： 01 int _stdcall MyFunction(int iVariant) 02 { 03 return 0; 04 } 为了导出上面这个函数，我们有以下几个方法： 1. 使用传统的模块定义文件 (.def) 新建一个 后缀为.def的文本文件(这里建一个TestDll.Def)，文件内容为： LIBRARY TestDll EXPORTS MyFunction 在 Link 时指定输入依赖文件：/DEF:"TestDll.Def" 2. Visual C++ 提供的方便方法 在01行的int 前加入 __declspec(dllexport) 关键字 通过以上两种方法，我们就可以导出MyFunction函数。 我们用Dependency查看导出的函数： 第一种方法导出的函数为： MyFunction 第二种方法导出的函数为： _MyFunction@4 __stdcall会使导出函数名字前面加一个下划线，后面加一个@再加上参数的字节数，比如_MyFunction@4的参数（int iVariant）就是4个字节 __fastcall与 __stdcall类似，不过前面没有下划线，而是一个@，比如@MyFunction@4 __cdecl则是始函数名。 小结：如果要导出C文件中的函数，并且不让编译器改动函数名，用def文件导出函数。 下面我们来看一下C++文件 我们用VS2008新建个DLL工程，工程名为"TestDLL" 默认的源文件后缀为 .CPP （即C++文件）。 输入测试代码如下： 01 int _stdcall MyFunction(int iVariant) 02 { 03 return 0; 04 } 为了导出上面这个函数，我们有以下几个方法： 3. 使用传统的模块定义文件 (.def) 新建一个 后缀为.def的文本文件(这里建一个TestDll.Def)，文件内容为： LIBRARY TestDll EXPORTS MyFunction 在 Link 时指定输入依赖文件：/DEF:"TestDll.Def" 4. Visual C++ 提供的方便方法 在01行的int 前加入 __declspec(dllexport) 关键字 通过以上两种方法，我们就可以导出MyFunction函数。 我们用Dependency查看导出的函数： 第一种方法导出的函数为： MyFunction 第二种方法导出的函数为： ?MyFunction@@YGHH@Z 可以看到 第二种方法得到的 导出函数名 并不是我们想要的，如果在exe中用显示方法（LoadLibrary、GetProcAddress）调用 MyFunction 肯定会失败。 但是用引入库(*.LIB)的方式调用，则编译器自动处理转换函数名，所以总是没有问题。 解决这个问题的方法是： 用VC 提供的预处理指示符 "#pragma" 来指定链接选项。 如下： #pragma comment(linker, "/EXPORT:MyFunction=?MyFunction@@YGHH@Z") 这时，就会发现导出的函数名字表中已经有了我们想要的MyFunction。但我们发现原来的那个 ?MyFunction@@YGHH@Z 函数还在，这时就可以把 __declspec() 修饰去掉，只需要 pragma 指令即可。 而且还可以使如下形式： #pragma comment(linker, "/EXPORT:MyFunction=_MyFunction@4,PRIVATE") PRIVATE 的作用与其在 def 文件中的作用一样。更多的#pragram请查看MSDN。 小结：如果要导出C++文件中的函数，并且不让编译器改动函数名，用def文件导出函数。 同时可以用#pragma指令（C 中也可以用）。 总结： C++编译器在生成DLL时，会对导出的函数进行名字改编，并且不同的编译器使用的改编规则不一样，因此改编后的名字也是不同的（一般涉及到C++ 中的重载等）。 如果利用不同编译器分别生成DLL和访问DLL的exe程序，后者在访问该DLL的导出函数时就会出现问题。如上例中函数MyFunction在C++编译器改编后的名字是?MyFunction@@YGHH@Z。我们希望编译后的名字不发生改变，这里有几种方法。 第一种方法是通过一个称为模块定义文件DEF来解决。 LIBRARY TestDll EXPORTS MyFunction LIBRARY 用来指定动态链接库内部名称。该名称与生成的动态链接库名一定要匹配，这句代码不是必须的。 EXPORTS说明了DLL将要导出的函数，以及为这些导出函数指定的符号名。 第二种是定义导出函数时加上限定符：extern "C" 如：#define DLLEXPORT_API extern "C" _declspec(dllexport) 但extern "C"只解决了C和C++语方之间调用的问题(extern "C" 是告诉编译器，让它按C的方式编译)，它只能用于导出全局函数这种情况 而不能导出一个类的成员函数。 同时如果导出函数的调用约定发生改变，即使使用extern "C"，编译后的函数名还是会发生改变。例如上面我们加入_stdcall关键字说明调用约定（标准调用约定，也就是WINAPI调用约定）。 #define DLLEXPORT_API extern "C" _declspec(dllexport) 01 DLLEXPORT_API int _stdcall MyFunction(int iVariant) 02 { 03 return 0; 04 } 编译后函数名MyFunction改编成了_MyFunction@4 通过第一种方法模块定义文件的方式DLL编译后导出函数名不会发生改变。 DLL(动态库)导出函数名乱码含义 C++编译时函数名修饰约定规则：   __stdcall调用约定：   1、以"?"标识函数名的开始，后跟函数名；    2、函数名后面以"@@YG"标识参数表的开始，后跟参数表；    3、参数表以代号表示：   X--void   D--char   E--unsigned char   F--short   H--int   I--unsigned int   J--long   K--unsigned long   M--float   N--double   _N--bool   ....   PA--表示指针，后面的代号表明指针类型，如果相同类型的指针连续出现，以"0"代替，一个"0"代表一次重复；   4、参数表的第一项为该函数的返回值类型，其后依次为参数的数据类型,指针标识在其所指数据类型前；    5、参数表后以"@Z"标识整个名字的结束，如果该函数无参数，则以"Z"标识结束。   其格式为"?functionname@@YG*****@Z"或"?functionname@@YG*XZ"，例如                       int Test1(char *var1, unsigned long)-----"?Test1@@YGHPADK@Z"                      void Test2()-----"?Test2@@YGXXZ"    __cdecl调用约定：   规则同上面的_stdcall调用约定，只是参数表的开始标识由上面的"@@YG"变为"@@YA"。   __fastcall调用约定：   规则同上面的_stdcall调用约定，只是参数表的开始标识由上面的"@@YG"变为"@@YI"。   如果要用DEF文件输出一个"C++"类，则把要输出的数据和成员的修饰名都写入.def模块定义文件   所以...   通过def文件来导出C++类是很麻烦的,并且这个修饰名是不可避免的

C只管函数名，不管函数参数和返回值。


Question: 求助 如何在C语言中读取想要的数据？

如果你需要多次比较，可以用链表或者结构体数组存储多个航班信息 如果只需要比较一次，那么文件读一行和指定的航班比较一次就行了

引用 3 楼 libinden71 的回复:这问题很简单啊！  定义FILE *指针  fopen打开文件    feof循环文件直到结尾 fgets读取文件内容   struct 定义结构把fgets读取出来的文件内容 植入 结构体中 判断航班号 输出字符串  可是有多个航班信息，怎么把他们都提取到结构体中？

好的，问题解决啦，谢谢各位大神！

仅供参考：//NAME: essaie bla bla //DIMENSION: 8 //DATA //1  14  15 //2  11  10 //3  6   4 //4  7   13 //5  9   21 //6  19  3 //7  1   5 //8  8   8 //EOF // // 文本文件中可能还含有其他内容，但是需要用到的内容即以上  //比如data.txt: //NAME: essaie bla bla //其它内容 //DIMENSION: 8 //其它内容 //DATA //其它内容 //1  14  15 //其它内容 //2  11  10 //其它内容 //3  6   4 //其它内容 //4  7   13 //其它内容 //5  9   21 //其它内容 //6  19  3 //其它内容 //7  1   5 //其它内容 //8  8   8 //其它内容 //EOF  // 目标是要获取NAME后字串，DIMENSION后数值，以及DATA以下的数值 // 其中NAME就是随便个字句，DIMENSION是城市数量，DATA以下是城市编号，X坐标，Y坐标 // 所有的这些将赋值给一个事先定义好的结构 #include <stdio.h> #include <string.h> #define MAXCPL   80   //每行最大字符数 #define MAXCITY  100  //每组数据中DATA最多项数，DIMENSION的最大值 #define MAXNAMEL 32   //NAME最大长度 struct S {     char NAME[MAXNAMEL+1];     int  DIMENSION;     struct D {         int NO;         int X;         int Y;     } DATA[MAXCITY]; } s; FILE *f; int st,n,i; char ln[MAXCPL]; int main() {     f=fopen("data.txt","r");     if (NULL==f) {         printf("Can not open file data.txt!\n");         return 1;     }     st=0;     n=0;     while (1) {         if (NULL==fgets(ln,MAXCPL,f)) break;         if (st==0) {             if (1==sscanf(ln,"NAME: %32[^\n]",s.NAME)) st=1;         } else if (st==1) {             if (1==sscanf(ln,"DIMENSION: %d",&s.DIMENSION)) st=2;         } else if (st==2) {             if (0==strcmp(ln,"DATA\n")) st=3;         } else if (st==3) {             if (3==sscanf(ln,"%d%d%d",&s.DATA[n].NO,&s.DATA[n].X,&s.DATA[n].Y)) {                 n++;                 if (n>=MAXCITY || n>=s.DIMENSION) break;             }         }     }     fclose(f);     printf("s.NAME=[%s]\n",s.NAME);     printf("s.DIMENSION=%d\n",s.DIMENSION);     for (i=0;i<n;i++) {         printf("s.DATA[%d].NO,X,Y=%d,%d,%d\n",i,s.DATA[i].NO,s.DATA[i].X,s.DATA[i].Y);     }     return 0; } //s.NAME=[essaie bla bla] //s.DIMENSION=8 //s.DATA[0].NO,X,Y=1,14,15 //s.DATA[1].NO,X,Y=2,11,10 //s.DATA[2].NO,X,Y=3,6,4 //s.DATA[3].NO,X,Y=4,7,13 //s.DATA[4].NO,X,Y=5,9,21 //s.DATA[5].NO,X,Y=6,19,3 //s.DATA[6].NO,X,Y=7,1,5 //s.DATA[7].NO,X,Y=8,8,8

可以用fopen,fread把文件里的数据一条条读出来，然后和用户输入的航班号进行比较