473,386 Members | 1,775 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,386 software developers and data experts.

Contrived casting situation

The thread where casting is being discussed motivated me to try this. Let's
say you wanted to populate a BankRecord structure (as I defined below) with
17-character record numbers, but with the record numbers separated into a SSN
and an account number (and with a terminating '\0')...

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

typedef struct {
char SSN[9];
char AccountNum[8];
char end;
} BankRecord;

int main( int argc, char * argv[] )
{
int i;
BankRecord *a;

if( argc < 2 ) {
printf( "No information provided\n" );
return EXIT_FAILURE;
}
if( (a=malloc((argc-1)*(sizeof(BankRecord)))) == NULL ) {
printf( "Malloc() failed\n" );
return EXIT_FAILURE;
}
for( i=1; i < argc; i++ ) {
snprintf( (char*)&a[i-1], sizeof(BankRecord), "%s", argv[i] );
}
for( i=0; i < argc-1; i++ ) {
printf( "%s\n", (char*)&a[i] ); /* just to prove that it works */
}
return EXIT_SUCCESS;
}

1) Is this code legal C? (it compiled with no warnings for me and worked
correctly)
2) Is the cast of a structure to a char* the best way to solve this contrived
problem?
3) How can I declare BankRecord in such a way so that end is a const char
equal to '\0'? Would that be desirable?
4) Any other comments?

--
Christopher Benson-Manica | Jumonji giri, for honour.
ataru(at)cyberspace.org |
Nov 13 '05 #1
13 1906
Christopher Benson-Manica wrote:

The thread where casting is being discussed motivated me to try this. Let's
say you wanted to populate a BankRecord structure (as I defined below) with
17-character record numbers, but with the record numbers separated into a SSN
and an account number (and with a terminating '\0')...

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

typedef struct {
char SSN[9];
char AccountNum[8];
char end;
} BankRecord;

int main( int argc, char * argv[] )
{
int i;
BankRecord *a;

if( argc < 2 ) {
printf( "No information provided\n" );
return EXIT_FAILURE;
}
if( (a=malloc((argc-1)*(sizeof(BankRecord)))) == NULL ) {
printf( "Malloc() failed\n" );
return EXIT_FAILURE;
}
for( i=1; i < argc; i++ ) {
snprintf( (char*)&a[i-1], sizeof(BankRecord), "%s", argv[i] );
}
for( i=0; i < argc-1; i++ ) {
printf( "%s\n", (char*)&a[i] ); /* just to prove that it works */
}
return EXIT_SUCCESS;
}

1) Is this code legal C? (it compiled with no warnings for me and worked
correctly)
The code is "legal" in the sense that it exhibits no
undefined behavior (unless I've missed something). However,
it is not strictly conforming because it has implementation-
defined behavior: sizeof(BankRecord) is at least 18, but
might be larger because of padding within the struct. Thus,
the program's output when given a 20-character command-line
argument, say, depends on the details of the implementation.

In short: It is not guaranteed that the ninth input
character lands in the AccountNum[0] spot; it might instead
land in the limbo between SSN and AccountNum. The code
works, but might not be doing what you want.
2) Is the cast of a structure to a char* the best way to solve this contrived
problem?
No, because it invokes the implementation-defined behavior
mentioned above and is thus not portable.
3) How can I declare BankRecord in such a way so that end is a const char
equal to '\0'? Would that be desirable?
There's no way to achieve this with a declaration. Whether
that's a desirable state of affairs is a matter of taste -- if
you want C++ and constructors, you know where to find them.
4) Any other comments?


Yes: Write what you mean, not something else that you sort
of think might produce the same effect. If you've got two
distinct values -- SSN and AccountNum -- treat them as such.
If you want to view them as sub-fields of a single larger
string, declare a single 18-element array to contain that
single larger string.

A guess: Did you cut your teeth on FORTRAN, and perhaps
become overly fond of the EQUIVALENCE declaration? If so,
my recommendation is to lose that habit when writing C.

--
Er*********@sun.com
Nov 13 '05 #2
Christopher Benson-Manica wrote:
The thread where casting is being discussed motivated me to try this. Let's
say you wanted to populate a BankRecord structure (as I defined below) with
17-character record numbers, but with the record numbers separated into a SSN
and an account number (and with a terminating '\0')...

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

typedef struct {
char SSN[9];
char AccountNum[8];
char end;
} BankRecord;

int main( int argc, char * argv[] )
{
int i;
BankRecord *a;

if( argc < 2 ) {
printf( "No information provided\n" );
return EXIT_FAILURE;
}
if( (a=malloc((argc-1)*(sizeof(BankRecord)))) == NULL ) {
printf( "Malloc() failed\n" );
return EXIT_FAILURE;
}
for( i=1; i < argc; i++ ) {
snprintf( (char*)&a[i-1], sizeof(BankRecord), "%s", argv[i] );
}
for( i=0; i < argc-1; i++ ) {
printf( "%s\n", (char*)&a[i] ); /* just to prove that it works */
}
return EXIT_SUCCESS;
}

1) Is this code legal C? (it compiled with no warnings for me and worked
correctly)
Yep.
2) Is the cast of a structure to a char* the best way to solve this contrived
problem?
You could use a union, but I don't think that would be better.
3) How can I declare BankRecord in such a way so that end is a const char
equal to '\0'? Would that be desirable?
You can't. It would be desirable in a way, but I think it would create
a lot of complexities in the language that would quickly defeat its
desirability.
4) Any other comments?


I think if the argument passed is too long, the a[i-1] in the snprintf
call will end up null-less. I think maybe sscanf would be better because
you can specify the length in the format specifier and it will append a
null even if the entire length is used. Other than that I think its a
valid piece of code.

Matt Gregory

Nov 13 '05 #3


Christopher Benson-Manica wrote:
The thread where casting is being discussed motivated me to try this. Let's
say you wanted to populate a BankRecord structure (as I defined below) with
17-character record numbers, but with the record numbers separated into a SSN
and an account number (and with a terminating '\0')...

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

typedef struct {
char SSN[9];
char AccountNum[8];
char end;
} BankRecord;

int main( int argc, char * argv[] )
{
int i;
BankRecord *a;

if( argc < 2 ) {
printf( "No information provided\n" );
return EXIT_FAILURE;
}
if( (a=malloc((argc-1)*(sizeof(BankRecord)))) == NULL ) {
printf( "Malloc() failed\n" );
return EXIT_FAILURE;
}
for( i=1; i < argc; i++ ) {
snprintf( (char*)&a[i-1], sizeof(BankRecord), "%s", argv[i] );
}
for( i=0; i < argc-1; i++ ) {
printf( "%s\n", (char*)&a[i] ); /* just to prove that it works */
You should try another test:
printf("AccountNum = %s\n",a[i].Accountum);
and see if it still works as expected.
}
return EXIT_SUCCESS;
}

1) Is this code legal C? (it compiled with no warnings for me and worked
correctly)
2) Is the cast of a structure to a char* the best way to solve this contrived
problem?


No, You have issues of struct member padding which are address in
fag questions 2.13 and 2.12

http://www.eskimo.com/~scs/C-faq/q2.13.html
http://www.eskimo.com/~scs/C-faq/q2.12.html

--
Al Bowers
Tampa, Fl USA
mailto: xa*@abowers.combase.com (remove the x)
http://www.geocities.com/abowers822/

Nov 13 '05 #4
Eric Sosman wrote:

Christopher Benson-Manica wrote:

[...]
typedef struct {
char SSN[9];
char AccountNum[8];
char end;
} BankRecord;

[...]

In short: It is not guaranteed that the ninth input
character lands in the AccountNum[0] spot; it might instead
land in the limbo between SSN and AccountNum.


Um, er, "Oops." The ninth input character necessarily
lands in SSN[8]; it's the *tenth* character whose location
is uncertain. Sorry about that.

--
Er*********@sun.com
Nov 13 '05 #5
Matt Gregory <ms********@earthlink.net> spoke thus:
You can't. It would be desirable in a way, but I think it would create
a lot of complexities in the language that would quickly defeat its
desirability.
Just making sure, thanks :)
I think if the argument passed is too long, the a[i-1] in the snprintf
call will end up null-less. I think maybe sscanf would be better because
you can specify the length in the format specifier and it will append a
null even if the entire length is used. Other than that I think its a
valid piece of code.


According to my documentation for snprintf(), it writes size-1 characters and
the size-th character is guaranteed to be '\0', so if sizeof(BankRecord) is
18, I'm fine. Of course, as the previous reply noted, that isn't guaranteed
to be the case...

--
Christopher Benson-Manica | Jumonji giri, for honour.
ataru(at)cyberspace.org |
Nov 13 '05 #6
Eric Sosman <Er*********@sun.com> spoke thus:
Um, er, "Oops." The ninth input character necessarily
lands in SSN[8]; it's the *tenth* character whose location
is uncertain. Sorry about that.


So structure fields are not guaranteed to be contiguous? How unfortunate :(

--
Christopher Benson-Manica | Jumonji giri, for honour.
ataru(at)cyberspace.org |
Nov 13 '05 #7
Christopher Benson-Manica <at***@nospam.cyberspace.org> scribbled the following:
Eric Sosman <Er*********@sun.com> spoke thus:
Um, er, "Oops." The ninth input character necessarily
lands in SSN[8]; it's the *tenth* character whose location
is uncertain. Sorry about that.
So structure fields are not guaranteed to be contiguous? How unfortunate :(


No, they aren't. The implementation is free to add any padding it likes
between any two members, or after the last member. The first member
MUST begin at offset 0, though.

--
/-- Joona Palaste (pa*****@cc.helsinki.fi) ---------------------------\
| Kingpriest of "The Flying Lemon Tree" G++ FR FW+ M- #108 D+ ADA N+++|
| http://www.helsinki.fi/~palaste W++ B OP+ |
\----------------------------------------- Finland rules! ------------/
"That's no raisin - it's an ALIEN!"
- Tourist in MTV's Oddities
Nov 13 '05 #8
Eric Sosman wrote:
Christopher Benson-Manica wrote:
1) Is this code legal C? (it compiled with no warnings for me and worked
correctly)

The code is "legal" in the sense that it exhibits no
undefined behavior (unless I've missed something). However,
it is not strictly conforming because it has implementation-
defined behavior: sizeof(BankRecord) is at least 18, but
might be larger because of padding within the struct. Thus,
the program's output when given a 20-character command-line
argument, say, depends on the details of the implementation.


Oh, I forgot about that. Oops. This is the very thing that
drove me nuts the first time I tried to translate a Pascal
program to C which wrote records to disk by calling Write().
Damn. Never did get that debugged. Had to switch back to
Pascal.

Matt Gregory

Nov 13 '05 #9
Joona I Palaste wrote:

Christopher Benson-Manica <at***@nospam.cyberspace.org> scribbled the following:
Eric Sosman <Er*********@sun.com> spoke thus:
Um, er, "Oops." The ninth input character necessarily
lands in SSN[8]; it's the *tenth* character whose location
is uncertain. Sorry about that.

So structure fields are not guaranteed to be contiguous? How unfortunate :(


No, they aren't. The implementation is free to add any padding it likes
between any two members, or after the last member. The first member
MUST begin at offset 0, though.

The case for padding in structures is for alignment of multibyte
objects, int, long, double, etc. The char is a single-byte object. It
can appear anywhere and has no known alignment requirements, even as an
array. There need be no padding between char objects. I betcha... sizeof
(BankRecord) == 18 on every implementation we have.
--
Joe Wright mailto:jo********@earthlink.net
"Everything should be made as simple as possible, but not simpler."
--- Albert Einstein ---
Nov 13 '05 #10
Joe Wright <jo********@earthlink.net> scribbled the following:
Joona I Palaste wrote:
Christopher Benson-Manica <at***@nospam.cyberspace.org> scribbled the following:
> Eric Sosman <Er*********@sun.com> spoke thus:
>> Um, er, "Oops." The ninth input character necessarily
>> lands in SSN[8]; it's the *tenth* character whose location
>> is uncertain. Sorry about that.

> So structure fields are not guaranteed to be contiguous? How unfortunate :(


No, they aren't. The implementation is free to add any padding it likes
between any two members, or after the last member. The first member
MUST begin at offset 0, though.

The case for padding in structures is for alignment of multibyte
objects, int, long, double, etc. The char is a single-byte object. It
can appear anywhere and has no known alignment requirements, even as an
array. There need be no padding between char objects. I betcha... sizeof
(BankRecord) == 18 on every implementation we have.


As long as "anywhere" means "anywhere after the first member", that is
true. There need be no padding between char objects, but there CAN be.
Arrays, OTOH, are required to be contiguous and padding-free.

--
/-- Joona Palaste (pa*****@cc.helsinki.fi) ---------------------------\
| Kingpriest of "The Flying Lemon Tree" G++ FR FW+ M- #108 D+ ADA N+++|
| http://www.helsinki.fi/~palaste W++ B OP+ |
\----------------------------------------- Finland rules! ------------/
"He said: 'I'm not Elvis'. Who else but Elvis could have said that?"
- ALF
Nov 13 '05 #11
Christopher Benson-Manica wrote:
So structure fields are not guaranteed to be contiguous? How unfortunate :(

See:

http://www.eskimo.com/~scs/C-faq/q2.12.html

http://www.eskimo.com/~scs/C-faq/q2.13.html


Brian Rodenborn
Nov 13 '05 #12
In message <3F***********@earthlink.net>
Joe Wright <jo********@earthlink.net> wrote:
typedef struct {
char SSN[9];
char AccountNum[8];
char end;
} BankRecord;


The case for padding in structures is for alignment of multibyte
objects, int, long, double, etc. The char is a single-byte object. It
can appear anywhere and has no known alignment requirements, even as an
array. There need be no padding between char objects. I betcha... sizeof
(BankRecord) == 18 on every implementation we have.


My most commonly used platform, which is ARM-based, has
sizeof(BankRecord) == 20, as all aggregate types are a whole number of 32-bit
words, and 32-bit aligned. This makes life easier when doing structure
assignments and initialisation, as it can use "load/store multiple word"
instructions, which only access word-aligned and sized data.

This may be atypical, but it suits the target architecture, and the C
standard is thankfully designed to allow implementations to make choices like
this.

The only pitfall is the usual one of programmers assuming, like you, details
of the structure padding. One example is the BSD <arpa/tftp.h> header file
which contains:

struct tftphdr {
short tur_opcode; /* TFTP opcode */
/******** OOPS - 2 bytes of unexpected padding here *******/
union {
struct {
char tur_stuff[1]; /* RRQ/WRQ/OACK params */
} tu_rq;
struct {
short tud_block; /* block# or error code */
char tud_data[1]; /* data or error string */
} tu_data;
} th_u;
}; /* sizeof(struct tftphdr) == 8 */

--
Kevin Bracey, Principal Software Engineer
Tematic Ltd Tel: +44 (0) 1223 503464
182-190 Newmarket Road Fax: +44 (0) 1223 503458
Cambridge, CB5 8HE, United Kingdom WWW: http://www.tematic.com/
Nov 13 '05 #13
Kevin Bracey wrote:

In message <3F***********@earthlink.net>
Joe Wright <jo********@earthlink.net> wrote:
typedef struct {
char SSN[9];
char AccountNum[8];
char end;
} BankRecord;


The case for padding in structures is for alignment of multibyte
objects, int, long, double, etc. The char is a single-byte object. It
can appear anywhere and has no known alignment requirements, even as an
array. There need be no padding between char objects. I betcha... sizeof
(BankRecord) == 18 on every implementation we have.


My most commonly used platform, which is ARM-based, has
sizeof(BankRecord) == 20, as all aggregate types are a whole number of 32-bit
words, and 32-bit aligned. This makes life easier when doing structure
assignments and initialisation, as it can use "load/store multiple word"
instructions, which only access word-aligned and sized data.

This may be atypical, but it suits the target architecture, and the C
standard is thankfully designed to allow implementations to make choices like
this.

The only pitfall is the usual one of programmers assuming, like you, details
of the structure padding. One example is the BSD <arpa/tftp.h> header file
which contains:

struct tftphdr {
short tur_opcode; /* TFTP opcode */
/******** OOPS - 2 bytes of unexpected padding here *******/
union {
struct {
char tur_stuff[1]; /* RRQ/WRQ/OACK params */
} tu_rq;
struct {
short tud_block; /* block# or error code */
char tud_data[1]; /* data or error string */
} tu_data;
} th_u;
}; /* sizeof(struct tftphdr) == 8 */

You are, of course, correct. My unthoughtful assumption that the world
is like Intel or SPARC coerces me to commit these errors. I am truly
sorry.
--
Joe Wright mailto:jo********@earthlink.net
"Everything should be made as simple as possible, but not simpler."
--- Albert Einstein ---
Nov 13 '05 #14

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

231
by: Brian Blais | last post by:
Hello, I saw on a couple of recent posts people saying that casting the return value of malloc is bad, like: d=(double *) malloc(50*sizeof(double)); why is this bad? I had always thought...
19
by: Ramesh Tharma | last post by:
Hi, Is any one knows what's wrong with the following code, I was told that it will compile and run but it will crash for some values. Assume that variables are initilized. char* c; long*...
4
by: c | last post by:
Hello All, I am struggling with simple casting issue within c# and would really appreciate some insight into where I am going wrong. I have two classes, ClassA and ClassB. Each implements a...
33
by: Mark P | last post by:
A colleague asked me something along the lines of the following today. For some type X he has: X* px = new X; Then he wants to convert px to a char* (I'm guessing for the purpose of...
5
by: brekehan | last post by:
I've always been a little sketchy on the differences between static, dynamic, and reinterpret casting. I am looking to clean up the following block by using C++ casting instead of the C style...
5
by: johanatan | last post by:
Does anyone know the reasons for the lack of an implicit casting operator in any greater depth than: A. Automatic conversion is believed to be too error prone. (from the FAQ at the bottom of:...
9
by: Taras_96 | last post by:
Hi everyone, I was experimenting with static_cast and reinterpret cast #include <iostream> struct A1 { int a; }; struct A2 { double d; }; struct B : public A1, A2
5
by: jason.cipriani | last post by:
There have been some recent threads about casting pointers to and from void* that have me rethinking some of my usual practices. I have a couple of questions. 1. What is the purpose of C++'s...
19
by: =?Utf-8?B?WWFua2VlIEltcGVyaWFsaXN0IERvZw==?= | last post by:
I'm doing my c# more and more like i used to code c++, meaning i'm casting more often than creating an instance of objects. like : protected void gvOrderDetailsRowDataBound(object sender,...
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.