jiayi Rss

struct 之 地址寻址与地址转换

Posted by jiayi | Posted in C/C++ | Posted on 16-01-2009

7

考完最后一科,嘎嘎,终于考完了~这个学期再也不敢懈怠考试了,抓了一段功课,有些效果。。恩,博客恢复更新~
说说C语言的 struct 吧。虽然基本,但有些东西还是挺有意思的。先看看 struct 的地址有哪些有意思的东西吧~

下面有一段代码,其中先给出一个 _student 结构体,声明后付初值。用 struct 的”点(.)”操作符打印其中 name 对象的地址与字符串。然后通过类似 struct 0 地址寻址的方式打,这里给了四句话,其中一句用“毒瘤”注释,说明不可取…最后我们在struct 寻址的基础上截取打印 name 字符串,唔,这里有两句“毒瘤”。 

C code

#include<stdio.h>
int main()
{
    struct _student {
        int sex;
        char *name;
    } student;

    student.name="T-MAC";
    student.sex=1;
    struct _student haha=student;

    /* 一般打印 */
    printf("%p ",&student.name);
    printf("%s ",*(&student.name));

    /* struct寻址打印 */
    printf("正常加倍寻址:%p ",&student+sizeof(int));
    printf("强制算数寻址:%p ",(char *)&student+sizeof(int));
    //printf("毒瘤 %s ",*((char *)&student+sizeof(int))); /* 毒瘤 */
    printf("麦神%s ",*(struct _student *)((char *)&student+sizeof(int)));

#define NAME_ADDR  (struct _student *)((char *)&student+sizeof(int))

    /* struct寻址 + 偏移量 打印 */
    //printf("毒瘤 %s ",*NAME_ADDR+2); /* 毒瘤 */
    //printf("毒瘤 %s ",(char *)*NAME_ADDR+2); /* 毒瘤 */
    printf("麦神%s ",(char *)*((int *)NAME_ADDR)+2);
}

正常加倍寻址和强制算数寻址不难理解,只是在用此方法时稍微留下神。

第一句“毒瘤” 编译可以通过,但运行时报“断错误”。(char *)&student+sizeof(int)) 计算的地址是一个 char * 型,看来用它引用 struct 成员 是不可取的。
而其后的“麦神”语句通过将 (char *)&student+sizeof(int)) 转换成 struct _student * 的到了正确结果。

最后我们要对 “T-MAC” 使用偏移量方法进行截取打印。这里的第一个“毒瘤”语句也许是我们首先想到的,但编译失败。* NAME_ADDR 看似是对struct 成员 的引用,但实质是一个 struct 对象对 struct 对象 进行加法操作,显然是不允许的!
好吧,既然 struct 对象 进行偏移量的加法不可行,那我们强制将其转换为 char * 怎么样呢…额,第二醒目的“毒瘤”告诉我们不行。。原因如下:结构类型(包括 union)不是数量类型(Scalar Type)。只有在数量类型之间才能进行转换。数量类型包括算术类型和指针类型,算术类型由包括整数类型和浮点类型。哦,原来是类型不匹配。
既然两条路都不通,难道就实现不了吗…我们还有笨法,即最后一句“麦神”。从源头将那块地址转换为一个 int * 型,因为 指针 实际是一个整型数。如果大家觉得 intchar * 对应不够明确,将 (int *)NAME_ADDR 换成 (char **)NAME_ADDR 也是没问题的。

struct 结构体其他有趣的东西要等回家写咯~明一早走。Bless all of you. Bless myself ~

有趣的C语言函数指针

Posted by jiayi | Posted in C/C++ | Posted on 15-11-2008

19

C语言的函数指针还真是诡异啊,下面四个代码都能正常运行。。C 难得把语法放的这么宽,写下来以示纪念…

C code


#include<stdio.h>

void haha()
{
    printf("haha\n");
}

int main()
{
    void (*func)();
    func = haha;
    func();
}

C code


#include<stdio.h>

void haha()
{
    printf("haha\n");
}

int main()
{
    void (*func)();
    func = haha;
    (*func)();
}

C code


#include<stdio.h>

void haha()
{
    printf("haha\n");
}

int main()
{
    void (*func)();
    func = &haha;
    func();
}

C code


#include<stdio.h>

void haha()
{
    printf("haha\n");
}

int main()
{
    void (*func)();
    func = &haha;
    (*func)();
}

printf 星号(*)

Posted by jiayi | Posted in C/C++ | Posted on 16-10-2008

1

Today encountered asterisk (*) when refering the printf() function. I met with it long long ago, but what it is has gone away from my mind absolutely…What the man said about the asterisk (*) is as the follows:

The format of printf() is like this

%[flags][fldwidth][precision][lenmodifier]convtype

   Format of the format string
       The  format string is a character string, beginning and ending in its initial shift state, if any.  The format string is composed
       of zero or more directives: ordinary characters (not %), which are copied unchanged to the output stream; and conversion specifi-
       cations, each of which results in fetching zero or more subsequent arguments.  Each conversion specification is introduced by the
       character %, and ends with a conversion specifier.  In between there may be (in this order) zero or more flags, an optional mini-
       mum field width, an optional precision and an optional length modifier.

       The  arguments must correspond properly (after type promotion) with the conversion specifier.  By default, the arguments are used
       in the order given, where each `* ‘ and each conversion specifier asks for the next argument (and it is an error if insufficiently
       many arguments are given).  One can also specify explicitly which argument is taken, at each place where an argument is required,
       by writing `%m$’ instead of `%’ and `*m$’ instead of `*, where the decimal integer m denotes the position in the  argument  list
       of the desired argument, indexed starting from 1.  Thus,

           printf("%*d", width, num);  # width is passed to ‘*’ as the [fldwidth] of the argument num

       and

           printf("%2$*1$d", width, num);

       are  equivalent.   The second style allows repeated references to the same argument.  The C99 standard does not include the style
       using `$’, which comes from the Single Unix Specification.  If the style using `$’ is used, it must be used  throughout  for  all
       conversions  taking an argument and all width and precision arguments, but it may be mixed with `%%’ formats which do not consume
       an argument.  There may be no gaps in the numbers of arguments specified using `$’; for example, if arguments 1 and 3 are  speci-
       fied, argument 2 must also be specified somewhere in the format string.

Then I will write down the flags associated with printf() as my memo

The flags component of a conversion specification

Flag

Description

-

left-justify the output in the field

+

always display sign of a signed conversion

(space)

prefix by a space if no sign is generated

#

convert using alternate form (include 0x prefix for hex format, for example)

0

prefix with leading zeros instead of padding with spaces

The length modifier component of a conversion specification

Length modifier

Description

hh

signed or unsigned char

h

signed or unsigned short

l

signed or unsigned long or wide character

ll

signed or unsigned long long

j

intmax_t or uintmax_t

z

size_t

t

ptrdiff_t

L

long double

The conversion type component of a conversion specification

Conversion type

Description

d,i

signed decimal

o

unsigned octal

u

unsigned decimal

x,X

unsigned hexadecimal

f,F

double floating-point number

e,E

double floating-point number in exponential format

g,G

interpreted as f, F, e, or E, depending on value converted

a,A

double floating-point number in hexadecimal exponential format

c

character (with l length modifier, wide character)

s

string (with l length modifier, wide character string)

p

pointer to a void

n

pointer to a signed integer into which is written the number of characters written so far

%

a % character

C

wide character (an XSI extension, equivalent to lc)

S

wide character string (an XSI extension, equivalent to ls)

C中从(int)看表达式“贪婪法”

Posted by jiayi | Posted in C/C++ | Posted on 09-08-2008

1

在《Expert C Programming》中有这样一个表达式:apple = sizeof (int) * p;
第一反应是,先求得int类型的长度,然后与p相乘。可作者又抛出一种可能的解释,将p指向的内容转换成int,然后对其执行sizeof操作。到底那种解释正确,作者让读者自行解决……
其实这个问题的关键是,(int)被解释为( )运算符,还是被解释为(type)运算符。
以下为实验代码:

  1. #include<stdio.h>
  2. int main()
  3. {
  4.     char ch=‘a’;
  5.     int n=2;
  6.     char *p=&ch;
  7.     int apple1=sizeof((int) * p);
  8.     int apple2=sizeof (int) * n;  /*可以将其换成int apple2=sizeof (int) * p;看编译器有何反应*/
  9.     int apple3=n * (int) * p;
  10.     printf("apple1 is %dn",apple1);
  11.     printf("apple2 is %dn",apple2);
  12.     printf("apple3 is %dn",apple3);
  13. } 

该程序运行结果:
        apple1 is 4
        apple2 is 8
        apple3 is 194
由结果不难得出结论:编译器遇到"(",首先看其能否与其左侧的对象或者运算符组成表达式,如果能,则解释为优先级最高的( )运算符,如果不能组成表达式,则解释为(type)运算符。
    第8句,编译器应该这样解释:sizeof 是一运算符,遇到"(",又有“)”与之对应,可以组成表达式,所以(int)被分解为( )运算符和int作为参数,后面就是与p进行一个乘法运算。
    第9句,n是一左操作数,“*"是乘法运算符,而其右的“(”无法与之组成表达式,所以(int)被解释为(type)运算符与其右的*p结合,*p被解释为p所指向的内容。
感觉这个过程类似于词法分析中的“贪婪法”,jiayi在这里姑且偷偷认为是表达式的“贪婪法”……有不对之处,大虾们拍~
有篇介绍sizeof的文章很好,也记下来:http://blog.chinaunix.net/u/20828/showart_438003.html