void* 연산 도중 생긴 문제

junho park@Google의 이미지

안녕하세요. c코딩을 하다가 warning을 만났는데 어떻게 해결할지 모르겠어서 질문을 드립니다.

    void *m_log_buffer = pLogger->log_buffer + (pLogCell->opcode != 2) ? pLogger->lastLsn : pLogger->synceLsn;

warning : initialization makes pointer from integer without a cast

여기서 pLogger은 제가 만든 struct이고 pLogger->log_buffer 는 void*, pLogger->lastLsn, pLogger->syncedLsn은 unsigned int로 선언되어 있습니다.

    void *m_log_buffer = pLogger->log_buffer + pLogger->lastLsn ;

이코드를 쓰고 있었을 때는 저런 에러가 나지 않았었는데 그 이유도 설명해 주시면 감사하겠습니다.

--수정--

삼항 연산자 우선순위에 관련된 문제라는 것까지는 감을 잡았습니다.

shint의 이미지

http://codepad.org/htoZxBvI

#include <stdio.h>
 
typedef struct DF_LOG
{
    void * log_buffer;
    unsigned int lastLsn;
    unsigned int syncedLsn;
}ST_LOG;
 
typedef struct DF_CELL
{
    int opcode;
}ST_CELL;
 
 
int main()
{
 
    void * p1; //선언구현
    void * p2 = NULL; //초기화
 
 
    ST_LOG * pLogger = NULL;
    int nSize = sizeof(ST_LOG);
    pLogger = (ST_LOG*) malloc (nSize);
    pLogger->log_buffer = (void*) malloc (100);
    pLogger->lastLsn = 10;
    pLogger->syncedLsn = 20;
 
    ST_CELL* pLogCell = NULL;
    nSize = sizeof(ST_CELL);
    pLogCell = (ST_CELL*) malloc (nSize);
    pLogCell->opcode = 1;
 
//    void *m_log_buffer = pLogger->log_buffer + pLogger->lastLsn ;
    printf("pLogger->log_buffer %x\n", pLogger->log_buffer);
    printf("pLogger->log_buffer %x\n", pLogger->log_buffer + pLogger->lastLsn);
 
//명시적 형변환
int a;
int b = (int) a;
 
//묵시적 형변환
int c;
int d = c;
 
    printf("pLogger->log_buffer + (pLogCell->opcode != 2) %x\n", pLogger->log_buffer + (pLogCell->opcode != 2));
//    void *m_log_buffer = pLogger->log_buffer + (pLogCell->opcode != 2) ? pLogger->lastLsn : pLogger->syncedLsn;
    printf("pLogger->lastLsn %x\n", pLogger->lastLsn);
    printf("pLogger->synceLsn %x\n", pLogger->syncedLsn);
 
    int nCode = 1;
    int r = nCode != 2 ? 1 : 2;
    printf("결과 %d\n", r); //1
 
    nCode = 2;
    r = nCode != 2 ? 1 : 2;
    printf("결과 %d\n", r); //2
 
    free(pLogger->log_buffer);
    free(pLogger);
    free(pLogCell);
 
    return 0;
}
 
pLogger->log_buffer 8f150a8
pLogger->log_buffer 8f150b2
pLogger->log_buffer + (pLogCell->opcode != 2) 8f150a9
pLogger->lastLsn a
pLogger->synceLsn 14
결과 1
결과 2

----------------------------------------------------------------------------
젊음'은 모든것을 가능하게 만든다.

매일 1억명이 사용하는 프로그램을 함께 만들어보고 싶습니다.
정규 근로 시간을 지키는. 야근 없는 회사와 거래합니다.

각 분야별. 좋은 책'이나 사이트' 블로그' 링크 소개 받습니다. shintx@naver.com

...!의 이미지

연산자 우선 순위는 알고 계시고, 포인터와 int의 더하기 빼기는 포인터 연산으로 정의된다는 것도 알고 계실 것 같습니다. 그럼 각 subexpression이 평가되면 결과의 타입이 어떻게 될 지 차근 차근 따라가기만 하면 되지요.

첫 번째 경우:

void* = (int* + int) ? int : int =>
void* = int* ? int : int =>
void* = bool ? int : int =>
void* = int // not ok

두 번째 경우:

void* = int* + int =>
void* = int* // ok

ymir의 이미지

+ 가 ?: 보다 우선순위가 높습니다.
결과적으로 m_log_buffer 에는 pLogger->lastLsn 이 들어가게 되니..
pointer 에 integer 를 넣고 있다는 경고가 뜨는거죠.

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

 의 이미지

삼항 연산자를 쓸 때는 진짜 주의하세요.

우선순위가 일반적으로 생각하는 것보다 굉장히 낮습니다. C언어에서 이 연산자보다 낮은 연산자는 대입 연산자 종류들과 쉼표 연산자밖에 없어요.
삼항 연산자의 조건식 옆에 있는 덧셈 연산자가 먼저 묶이면서 질문자님이 올리신 코드는 이렇게 해석됩니다.

void *m_log_buffer = 
    (pLogger->log_buffer + (pLogCell->opcode != 2)) ?
        pLogger->lastLsn :
        pLogger->synceLsn;

이러면 조건식 부분((pLogger->log_buffer + (pLogCell->opcode != 2)))의 타입과 상관 없이 삼항 연산자의 결과 타입은 pLogger->lastLsnpLogger->synceLsn의 타입이 되고, 즉 unsigned int가 됩니다.

C언어에서 unsigned int에서 void *로의 캐스팅이 안 되는 것은 아닙니다만, 명시적인 캐스팅 없이 이렇게 하는 경우는 보통 거의 없기 때문에 컴파일러가 경고를 해 줍니다. 이 경우는 경고를 받아서 천만다행인 경우죠. 프로그래머의 의도와 전혀 다르게 컴파일되고 있으니까요.

? 바로 앞에 있는 수식만을 삼항 연산자의 조건식으로 삼으려고 괄호를 쓰셨겠지만, 저것만 감싸서는 의미가 없어요. 이렇게 쓰셨어야죠.

void *m_log_buffer = 
    pLogger->log_buffer +
    ((pLogCell->opcode != 2) ?
        pLogger->lastLsn :
        pLogger->synceLsn);

이 경우 삼항 연산자 다음에 덧셈이 일어나고, 즉 타입을 보면 void *unsigned int를 더하게 됩니다. 이러한 void * 산술에 대한 C언어 표준적 근거를 못 찾겠는데[1], 결과 타입이 void *이라고 가정해도 크게 무리가 없겠지요. 그 결과 자연스럽게 m_log_buffer에 저장되는 것입니다.

이러한 분석은 질문자님의 두 번째 코드 void *m_log_buffer = pLogger->log_buffer + pLogger->lastLsn ;에도 그대로 적용됩니다.

덧. 삼항 연산자 다음으로 우선순위가 낮은 연산자는 논리적 OR(||), 그 다음이 논리적 AND(&&)입니다. 이 둘을 복잡한 수식 가운데서 사용하는 것도 매우 주의해야 합니다.

[1] 포인터에 정수를 더하는 표현식은 포인터가 complete object type을 가리켜야만 한다고 되어 있는데, void는 incomplete object type이거든요. 뭔가 제가 찾지 못한 예외조항이 있거나 표준에서 이러한 표현식을 허용하지 않는 것 같습니다. 물론 그런 경우에도 컴파일러가 이와 같은 코드를 컴파일해 줄 수 있습니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.