C指针原理(46)-C应用技巧(1)

字符串小写转大写

#include <stdio> 

int main(void) 
{ 
      int i=0;
      char string[100];
      strcpy(string,"abcdefghijklmnopqrstuvwxyz");

 
 while (string[i]!='\0'){ //将小写转化成大写
              if (islower(string[i]))   
                  string[i]=toupper(string[i]); 
 
       i++; 
   } 
   printf("%s\n",string); 
   return 0; 
}

linux-C获得用户信息和节点信息
C代码  

#include <unistd.h>  
#include <sys/utsname.h>  
#include <sys/types.h>  
#include <pwd.h>  
int main(void){//  
    char hname[256];//节点名称  
     struct utsname uts;//节点结构信  
    uid_t uid;  
    gid_t gid;  
           struct passwd *pw;  
  
     if (gethostname(hname,255)!=0||uname(&uts)<0){  
       printf("不能得到主机信息");  
                  exit(1);  
         }  
  
  printf("主机名:%s\n",hname);  
  printf("系统名称:%s\n 机器名称:%s\n",uts.sysname,uts.machine);  
  printf("节点名称:%s\n",uts.nodename);  
  printf("版本:%s,版本号%s",uts.release,uts.version);//系统版本,版本号  
    //取得当前用户登陆情况  
     uid=getuid();  
     gid=getgid();  
     pw=getpwuid(uid);  
     printf("用户id 为%d,用户组为%d\n",uid,gid);  
     printf("用户真实姓名%s\n用户名称:%s\n",pw->pw_gecos,pw->pw_name);  
     printf("用户uid:%s\ngid:%s\n",pw->pw_uid,pw->pw_gid);             
     printf("主目录:%s\n",pw->pw_dir);               
     printf("用户shell:%s\n",pw->pw_shell);       
  
}  

 运行:

gcc -o test7 test7.c

test7.c: In function ‘main’:
test7.c:13: warning: incompatible implicit declaration of built-in function ‘printf’
test7.c:14: warning: incompatible implicit declaration of built-in function ‘exit’
test7.c:17: warning: incompatible implicit declaration of built-in function ‘printf’

./test7

主机名:puppypc
系统名称:Linux
 机器名称:i686
节点名称:puppypc
版本:2.6.30.5,版本号#1 SMP Tue Sep 1 15:48:26 GMT-8 2009用户id 为0,用户组为0
用户真实姓名root
用户名称:root
用户uid:(null)
gid:(null)
主目录:/root
用户shell:/bin/sh
用户密码:x
#

linux-C产生临时文件

#include <stdio.h>
int main(void){
   char tmpname[L_tmpnam];
   char *filename;
   FILE *fp;
   strcpy(tmpname,"/tmp/dfXXXXXX");//file name:df...
   filename=mktemp(tmpname);//generate tempfile
   printf("temporary file name:%s\n",filename);
   
   fp=tmpfile();
   if (fp) printf("temporary file oepn.\n");
   else perror("error");
   exit(0);  
}

linux-文件属性及目录基本操作

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <dirent.h>
int main(int argc,char *argv[]){
   int i;
   struct stat buf;
   char *ptr;
   if (argc<2){
       printf("filename error");
       exit(1);
   }
   if (lstat(argv[1],&buf)<0){//lstat和stat 判断文件属性,但lstat只判断连接文件本身,不追踪真实文件
       perror("lstat error");
   }
   if (S_ISREG(buf.st_mode)) ptr="普通文件";
   else if(S_ISDIR(buf.st_mode)) ptr="目录";
   else if(S_ISCHR(buf.st_mode)) ptr="字符设备";
   else if(S_ISFIFO(buf.st_mode)) ptr="有名管道";
   else if(S_ISLNK(buf.st_mode)) ptr="符号链接";
   else if(S_ISBLK(buf.st_mode)) ptr="块设备";
   else if(S_ISSOCK(buf.st_mode)) ptr="SOCKET";
   else ptr="未知设备";
   printf("FILE %s is %s file",argv[1],ptr);
   if (S_ISREG(buf.st_mode)){//如果是文件名
       printf("file size is %d bytes.\n",buf.st_size);
   }

   if (S_ISDIR(buf.st_mode)){//如果是目录名,则ls目录的文件
       DIR *dp;
       struct dirent *dirp;
       if ((dp=opendir(argv[1]))==NULL) perror("opendir error");
       while ((dirp=readdir(dp))!=NULL){
          printf("%s\n",dirp->d_name);   //输出目录下的文件名
       }
          closedir(dp);
          free(dirp);
   }

   return 0;
}

c-文件操作-文件位置

CC++C#FP
long file_pos=ftell(fp);//返回当前文件位置
fgetpos(fp,&fp_pos);//返回当前文件位置到fp_pos中
fsetpos(fp,&fp_pos);//设置当前文件位置为fp_pos
rewind(fp);//把文件置在起始处

linux-多线程-互斥锁在多进程共享

C代码  

#include <sys/stat.h>  
#include <fcntl.h>  
#include <sys/mman.h>  
#include <unistd.h>  
  
  
#include <pthread.h>  
#include <stdio.h>  
#include <stdlib.h>  
  
  
  
  
int main(void){//2个进程,一个进程完成每次加1,另一个进程完成每次加2,2个进程协作完成累加,使用共享内存方式在进程间通信  
  
int *x;  
int rt;  
int shm_id;  
char *addnum="myadd";  
char *ptr;  
  
pthread_mutex_t mutex;//互斥对象  
pthread_mutexattr_t mutexattr;//互斥对象属性  
  
  
   pthread_mutexattr_init(&mutexattr);//初始化互斥对象属性  
   pthread_mutexattr_setpshared(&mutexattr,PTHREAD_PROCESS_SHARED);//设置互斥对象为PTHREAD_PROCESS_SHARED共享,即可以在多个进程的线程访问,PTHREAD_PROCESS_PRIVATE为同一进程的线程共享  
   rt=fork();//复制父进程,并创建子进程   
//deepfuture.iteye.com,深未来技术原创  
   if (rt==0){//子进程完成x+1  
       shm_id=shm_open(addnum,O_RDWR,0);  
       ptr=mmap(NULL,sizeof(int),PROT_READ|PROT_WRITE,MAP_SHARED,shm_id,0);/*连接共享内存区*/  
       x=(int *)ptr;    
    
      for (int i=0;i<10;i++){//加10次。相当于加10  
       pthread_mutex_lock(&mutex);        
       (*x)++;  
       printf("x++:%d\n",*x);  
       pthread_mutex_unlock(&mutex);   
       sleep(1);                   
      }  
   }     
   else{//父进程完成x+2  
       shm_id=shm_open(addnum,O_RDWR|O_CREAT,0644);  
        ftruncate(shm_id,sizeof(int));  
        ptr=mmap(NULL,sizeof(int),PROT_READ|PROT_WRITE,MAP_SHARED,shm_id,0);/*连接共享内存区*/  
        x=(int *)ptr;   
            
      for (int i=0;i<10;i++){//加10次,相当于加20  
       pthread_mutex_lock(&mutex);         
       (*x)+=2;  
       printf("x+=2:%d\n",*x);  
       pthread_mutex_unlock(&mutex);    
       sleep(1);   
      }        
        
   }   
   shm_unlink(addnum);//删除共享名称  
   munmap(ptr,sizeof(int));//删除共享内存  
   return(0);  
}  

 编译
deepfuture@deepfuture-laptop:~/private/mytest$ gcc  -lpthread -std=c99 -lrt -o testmutex testmutex.c

执行

deepfuture@deepfuture-laptop:~/private/mytest$ ./testmutex
x+=2:2
x++:3
x+=2:5
x++:6
x+=2:8
x++:9
x+=2:11
x++:12
x+=2:14
x++:15
x+=2:17
x++:18
x+=2:20
x++:21
x+=2:23
x++:24
x+=2:26
x++:27
x+=2:29
x++:30

一、什么是共享内存区
共享内存区是最快的可用IPC形式。它允许多个不相关的进程去访问同一部分逻辑内存。如果需要在两个运行中的进程之间传输数据,共享内存将是一种效率极高的解决方案。一旦这样的内存区映射到共享它的进程的地址空间,这些进程间数据的传输就不再涉及内核。这样就可以减少系统调用时间,提高程序效率。
共享内存是由IPC为一个进程创建的一个特殊的地址范围,它将出现在进程的地址空间中。其他进程可以把同一段共享内存段“连接到”它们自己的地址空间里去。所有进程都可以访问共享内存中的地址。如果一个进程向这段共享内存写了数据,所做的改动会立刻被有访问同一段共享内存的其他进程看到。
要注意的是共享内存本身没有提供任何同步功能。也就是说,在第一个进程结束对共享内存的写操作之前,并没有什么自动功能能够预防第二个进程开始对它进行读操作。共享内存的访问同步问题必须由程序员负责。可选的同步方式有互斥锁、条件变量、读写锁、纪录锁、信号灯。
二、mmap
在讲共享内存前我们要先来介绍下面几个函数。
mmap函数把一个文件或一个Posix共享内存区对象映射到调用进程的地址空间。使用该函数有三个目的:
1.使用普通文件以提供内存映射I/O
2.使用特殊文件以提供匿名内存映射。
3.使用shm_open以提供无亲缘关系进程间的Posix共享内存区。
函数1.
名称:: mmap
功能: 把I/O文件映射到一个存储区域中
头文件: #include <sys/mman.h>
函数原形:
void mmap(void addr,size_t len,int prot,int flag,int filedes,off_t off);
参数:
addr      指向映射存储区的起始地址
len       映射的字节
prot      对映射存储区的保护要求
flag      flag标志位
filedes    要被映射文件的描述符
off       要映射字节在文件中的起始偏移量
返回值:
若成功则返回映射区的起始地址,若出错则返回MAP_FAILED
addr参数用于指定映射存储区的起始地址。通常将其设置为NULL,这表示由系统选择该映射区的起始地址。
filedes指要被映射文件的描述符。在映射该文件到一个地址空间之前,先要打开该文件。len是映射的字节数。
off是要映射字节在文件中的起始偏移量。通常将其设置为0。
prot参数说明对映射存储区的保护要求。可将prot参数指定为PROT_NONE,或者是PROT_READ(映射区可读),PROT_WRITE(映射区可写),PROT_EXEC(映射区可执行)任意组合的按位或,也可以是PROT_NONE(映射区不可访问)。对指定映射存储区的保护要求不能超过文件open模式访问权限。
flag参数影响映射区的多种属性:
MAP_FIXED 返回值必须等于addr.因为这不利于可移植性,所以不鼓励使用此标志。
MAP_SHARED 这一标志说明了本进程对映射区所进行的存储操作的配置。此标志指定存储操作修改映射文件。
MAP_PRIVATE 本标志导致对映射区建立一个该映射文件的一个私有副本。所有后来对该映射区的引用都是引用该副本,而不是原始文件。
要注意的是必须指定MAP_FIXED或MAP_PRIVATE标志其中的一个,指定前者是对存储映射文件本身的一个操作,而后者是对其副本进行操作。
mmap成功返回后,fd参数可以关闭。该操作对于由mmap建立的映射关系没有影响。为从某个进程的地址空间删除一个映射关系,我们调用munmap.
函数2.
名称:: munmap
功能: 解除存储映射
头文件: #include <sys/mman.h>
函数原形: int munmap(caddr_t addr,size_t len);
参数:
addr      指向映射存储区的起始地址
len       映射的字节
返回值: 若成功则返回0,若出错则返回-1
其中addr参数是由mmap返回的地址,len是映射区的大小。再次访问这些地址导致向调用进程产生一个SIGSEGV信号。
如果被映射区是使用MAP_PRIVATE标志映射的,那么调用进程对它所作的变动都被丢弃掉。
内核的虚存算法保持内存映射文件(一般在硬盘上)与内存映射区(在内存中)的同步(前提它是MAP_SHARED内存区)。这就是说,如果我们修改了内存映射到某个文件的内存区中某个位置的内容,那么内核将在稍后某个时刻相应地更新文件。然而有时候我们希望确信硬盘上的文件内容与内存映射区中的文件内容一致,于是调用msync来执行这种同步。
函数3.
名称:: msync
功能: 同步文件到存储器
头文件: #include <sys/mman.h>
函数原形: int msync(void addr,size_t len,int flags);
参数:
addr      指向映射存储区的起始地址
len       映射的字节
prot      flags
返回值: 若成功则返回0,若出错则返回-1
其中addr和len参数通常指代内存中的整个内存映射区,不过也可以指定该内存区的一个子集。flags参数为MS_ASYNC(执行异步写),MS_SYNC(执行同步写),MS_INVALIDATE(使高速缓存的数据实效)。其中MS_ASYNC和MS_SYNC这两个常值中必须指定一个,但不能都指定。它们的差别是,一旦写操作已由内核排入队列,MS_ASYNC即返回,而MS_SYNC则要等到写操作完成后才返回。如果还指定了MS_INVALIDATE,那么与其最终拷贝不一致的文件数据的所有内存中拷贝都失效。后续的引用将从文件取得数据。
函数4.
名称:: memcpy
功能: 复制映射存储区
头文件: #include <string.h>
函数原形: void
memcpy(void dest,const void src,size_t n);
参数:
dest       待复制的映射存储区
src        复制后的映射存储区
n          待复制的映射存储区的大小
返回值:
返回dest的首地址
memcpy拷贝n个字节从dest到src。

下面就是利用mmap函数影射I/O实现的cp命令。

/*mycp.c*/
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>

int main(int argc,char *argv[])
{
int fdin,fdout;
void *arc,dst;
struct stat statbuf;

if(argc!=3)
{
printf(“please input two file!\n”);
exit(1);
}
if((fdin=open(argv[1],O_RDONLY))<0) /*打开原文件*/
perror(argv[1]);
if((fdout=open(argv[2],O_RDWR|O_CREAT|O_TRUNC))<0)/*创建并打开目标文件*/
perror(argv[2]);

if(fstat(fdin,&statbuf)<0) /*获得文件大小信息*/
printf(“fstat error”);

if(lseek(fdout,statbuf.st_size-1,SEEK_SET)==-1)/*初始化输出映射存储区*/
printf(“lseek error”);
if(write(fdout,”1”)!=1)
printf(“write error”);

if((src=mmap(0,statbuf.st_size,PROT_READ,MAP_SHARED,fdin,0))==MAP_FAILED)
/*映射原文件到输入的映射存储区*/
printf(“mmap error);
if((dst=mmap(0,statbuf.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,fdout,0)) ==MAP_FAILED) /*映射目标文件到输出的映射存储区*/
printf(“mmap error);
memcpy(dst,src,statbuf.st_size);/*复制映射存储区*/
munmap(src,statbuf.st_size); /*解除输入映射*/
munmap(dst,statbuf.st_size); /*解除输出映射*/
close(fdin);``
close(fdout);
}

下面是运行结果:
#cc –o mycp mycp.c
#./mycp test1 test2

三、posix共享内存函数
posix共享内存区涉及两个步骤:
1、指定一个名字参数调用shm_open,以创建一个新的共享内存区对象或打开一个已存在的共享内存区对象。
2、调用mmap把这个共享内存区映射到调用进程的地址空间。传递给shm_open的名字参数随后由希望共享该内存区的任何其他进程使用。
函数5.
名称:: shm_open
功能: 打开或创建一个共享内存区
头文件: #include <sys/mman.h>
函数原形: int shm_open(const char name,int oflag,mode_t mode);
参数:
name    共享内存区的名字
cflag    标志位
mode    权限位
返回值: 成功返回0,出错返回-1
oflag参数必须含有O_RDONLY和O_RDWR标志,还可以指定如下标志:O_CREAT,O_EXCL或O_TRUNC.
mode参数指定权限位,它指定O_CREAT标志的前提下使用。
shm_open的返回值是一个整数描述字,它随后用作mmap的第五个参数。
函数6.
名称:: shm_unlink
功能: 删除一个共享内存区
头文件: #include <sys/mman.h>
函数原形: int shm_unlink(const char
name);
参数: name    共享内存区的名字
返回值: 成功返回0,出错返回-1
shm_unlink函数删除一个共享内存区对象的名字,删除一个名字仅仅防止后续的open,mq_open或sem_open调用取得成功。
示例:
下面是创建一个共享内存区的例子:
/shm_open.c创建共享内存区/
#include <sys/mman.h>
#include <stdio.h>
#include <fcntl.h>

int main(int argc,char **argv)
{
int shm_id;

if(argc!=2)
{
printf(“usage:shm_open <pathname>\n”);
exit(1);
}
shm_id=shm_open(argv[1],O_RDWR|O_CREAT,0644);
printf(“shmid:%d\n”,shm_id);
shm_unlink(argv[1]);
}
下面是运行结果,注意编译程序我们要加上“-lrt”参数。
#cc –lrt –o shm_open shm_open.c
#./shm_open test
shm_id:3
四、ftruncate和fstat函数
普通文件或共享内存区对象的大小都可以通过调用ftruncate修改。
函数7.
名称:: ftruncate
功能: 调整文件或共享内存区大小
头文件: #include <unistd.h>
函数原形: int ftruncate(int fd,off_t length);
参数:
fd          描述符
length       大小
返回值: 成功返回0,出错返回-1
当打开一个已存在的共享内存区对象时,我们可调用fstat来获取有关该对象的信息。
函数8.
名称:: fstat
功能: 获得文件或共享内存区的信息
头文件:
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
函数原形: int stat(const char file_name,struct stat buf);
参数:
file_name          文件名
buf               stat结构
返回值: 成功返回0,出错返回-1

对于普通文件stat结构可以获得12个以上的成员信息,然而当fd指代一个共享内存区对象时,只有四个成员含有信息。

struct stat{
mode_t st_mode;
uid_t st_uid;
gid_t st_gid;
off_t st_size;
};
/*shm_show.c显示共享区信息*/
#include <unistd.h>
#include <sys/type.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/mman.h>

int main(int argc,char **argv)
{
int shm_id;
struct stat buf;

if(argc!=2)
{
printf(“usage:shm_open <pathname>\n”);
exit(1);
}
shm_id=shm_open(argv[1],O_RDWR|O_CREAT,0644);/*创建共享内存*/
ftruncate(shm_id,100);/*修改共享内存的大小*/
fstat(shm_id,&buf); /*把共享内存的信息记录到buf中*/
printf(“uid_t:%d\n”,buf.st_uid); /*共享内存区所有者ID*/
printf(“git_t:%d\n”,buf.st_gid); /*共享内存区所有者组ID*/
printf(“size :%d\n”,buf.st_size); /*共享内存区大小*/
}

下面是运行结果:
#cc –lrt –o shm_show shm_show.c
#./shm_show test
uid_t:0
git_t:0
size:100

键盘缓冲区残余信息问题 

#include <stdio.h>
int main()
{
    int a;``
    char c; 
    do
    {
        scanf("%d",&a);
        scanf("%c",&c);
        printf("a=%d     c=%c\n",a,c);
        /*printf("c=%d\n",c);*/
    }while(c!=''N'');
} 

   scanf("%c",&c);这句不能正常接收字符,什么原因呢?我们用printf("c=%d\n",c);将C用int表示出来,启用printf("c=%d\n",c);这一句,看看scanf()函数赋给C到底是什么,结果是 c=10 ,ASCII值为10是什么?换行即\n.对了,我们每击打一下"Enter"键,向键盘缓冲区发去一个“回车”(\r),一个“换行"(\n),在这里\r被scanf()函数处理掉了(姑且这么认为吧^_^),而\n被scanf()函数“错误”地赋给了c. 
解决办法:可以在两个scanf()函数之后加个fflush(stdin);,还有加getch(); getchar();也可以,但是要视具体scanf()语句加那个,这里就不分析了,读者自己去摸索吧。但是加fflush(stdin);不管什么情况都可行。 
函数名: fflush 
功 能: 清除一个流 
用 法: int fflush(FILE *stream); 

#include <stdio.h>
int main()
{
    int a;
    char c; 
    do
    {
        scanf("%d",&a);
        fflush(stdin);
        scanf("%c",&c);
        fflush(stdin);
        printf("a=%d     c=%c\n",a,c); 
    }while(c!=''N'');
}      
这里再给一个用“空格符”来处理缓冲区残余信息的示例: 
运行出错的程序: 
#include <stdio.h>
int main()
{
    int i;
    char j;
    for(i = 0;i < 10;i++)
    {
        scanf("%c",&j);/*这里%前没有空格*/
    }
} 

使用了空格控制符后: 
#include <stdio.h>
int main()
{
    int i;
    char j;
    for(i = 0;i < 10;i++)
    {
        scanf(" %c",&j);/注意这里%前有个空格/
    }

    可以运行看看两个程序有什么不同。 

​​​​​​​高级版的取子串函数,可以完成正向取子串,反向取子串,
1、调用:
substr(取出的子串,源串,起始位置,长度)
函数返回实际取到子串的长度
其中长度可以为正数(从左边向右边取),长度为负数(从右边向左边取),长度可以超过实际能取到的子串长度,函数会智能判断,取长度范围内尽可能长的子串。

2、函数源代码
C代码  

int substr(char* dchr,char *schr,int begin,int len){  
//作者:myhaspl@myhaspl.com 
//取子串函数,dchr为取好后的子串,schr为源串,返回成功取出的子串数目,len为负数,则从begin向头部移动(正向),否则向尾部移动(反向),begin为起始位置(从1开始),len为子串长度  
  
    int slen=0;  
  
    int rc=0;      
  
    if (begin<=0) begin=1;//起始位置为0时,会从1开始  
  
    slen=strlen(schr)-begin;      
  
    if (slen<=0||len==0){//当len为0或begin的位置已经超过源串长度时,取空串  
  
       *dchr=NULL;  
  
       return rc;  
  
    }  
  
    if (len<0){//len为负数,表示从begin处向头部移动len个字符的子串,允许出现len移过头的现象(begin=3,len=-6,则取从位置1到位置3的子串)  
  
        len=-len;  
  
        if(len>strlen(schr)) begin=1;  
  
        else if((begin-len)<=0){  
  
            len=begin;  
  
            begin=1;  
  
        }  
  
        else {  
  
             begin-=len;  
  
             begin++;  
  
        }  
  
    }  
  
  
  
     begin--;  
  
     schr+=begin;  
  
     int i=0;  
  
     for(i=0;i<len&&*schr!=0;i++){  
  
        *dchr++ = *schr++;           
  
     }  
  
     *dchr=0;   
  
     rc=i;  
  
  return rc;  
  
}

c_预定义宏-反映编译信息
  

1、     LINE:被编译的文件中的行号

2、     FILE:编译的日期

3、     DATE:编译的日期("Mm dd yyyy")

4、     TIME:编译的时间("hh:mm:ss")

5、     STDC:如果编译器符合C标准(C89或C99),则值为1

C-#line和#error
1、
1)源代码:
test2.c
C代码  

#line 1  
int main(void){  
printf("line 1\n");  
printf("line 2\n");  
printf("line 3\n");  
printf("line 4\n");  
printf("line 5\n");  
printf("line 6\n");  
printf("line 7\n");  
printf("line 8\n");  
#include "test1.c  

 test1.c
 
C代码  

#line 9 "test2.c"  
#define LINUX  
#ifdef WIN32  
  printf("win32\n");     
#elif defined LINUX  
  printf("linux %d %s\n",__LINE__,__FILE__);          
#else  
  #error no flag define   
  //如果LINUX和WIN32没有定义,#error会显示错误信息,然后停止编译  
#endif  
}  

  2)运行结果:
mysea@mysea-pc:~/test$ ./test2
line 1
line 2
line 3
line 4
line 5
line 6
line 7
line 8
linux 13 test2.c

 
2、#error表示停止编译,显示错误信息
3、#line 直接指定下一行的行号及文件名
1)指定行号
#line n
2)指定文件名和行号
#line n "filename"
4、源代码的#include "test1.c"和#line 9 "test2.c"表示:test2.c和test1.c实质属于一个C程序:test2。
1)注意这只是标注,如果要在编译时把test1.c包括进来,必须加上#include "test1.c
 
 
2)编译时,编译test2
 
 
 
mysea@mysea-pc:~/test$ gcc -o test2 test2.c
 

上一篇:PYTHON2.day07


下一篇:Envoy 基础教程:使用 Unix Domain Socket(UDS) 与上游集群通信