Converting int to string in C - c

I am using the itoa() function to convert an int into string, but it is giving an error:
undefined reference to `itoa'
collect2: ld returned 1 exit status
What is the reason? Is there some other way to perform this conversion?

Use snprintf, it is more portable than itoa.
itoa is not part of standard C, nor is it part of standard C++; but, a lot of compilers and associated libraries support it.
Example of sprintf
char* buffer = ... allocate a buffer ...
int value = 4564;
sprintf(buffer, "%d", value);
Example of snprintf
char buffer[10];
int value = 234452;
snprintf(buffer, 10, "%d", value);
Both functions are similar to fprintf, but output is written into an array rather than to a stream. The difference between sprintf and snprintf is that snprintf guarantees no buffer overrun by writing up to a maximum number of characters that can be stored in the buffer.

Use snprintf - it is standard an available in every compilator. Query it for the size needed by calling it with NULL, 0 parameters. Allocate one character more for null at the end.
int length = snprintf( NULL, 0, "%d", x );
char* str = malloc( length + 1 );
snprintf( str, length + 1, "%d", x );
...
free(str);

Before I continue, I must warn you that itoa is NOT an ANSI function — it's not a standard C function. You should use sprintf to convert an int into a string.
itoa takes three arguments.
The first one is the integer to be converted.
The second is a pointer to an array of characters - this is where the string is going to be stored. The program may crash if you pass in a char * variable, so you should pass in a normal sized char array and it will work fine.
The last one is NOT the size of the array, but it's the BASE of your number - base 10 is the one you're most likely to use.
The function returns a pointer to its second argument — where it has stored the converted string.
itoa is a very useful function, which is supported by some compilers - it's a shame it isn't support by all, unlike atoi.
If you still want to use itoa, here is how should you use it. Otherwise, you have another option using sprintf (as long as you want base 8, 10 or 16 output):
char str[5];
printf("15 in binary is %s\n", itoa(15, str, 2));

Better use sprintf(),
char stringNum[20];
int num=100;
sprintf(stringNum,"%d",num);

Usually snprintf() is the way to go:
char str[16]; // could be less but i'm too lazy to look for the actual the max length of an integer
snprintf(str, sizeof(str), "%d", your_integer);

You can make your own itoa, with this function:
void my_utoa(int dataIn, char* bffr, int radix){
int temp_dataIn;
temp_dataIn = dataIn;
int stringLen=1;
while ((int)temp_dataIn/radix != 0){
temp_dataIn = (int)temp_dataIn/radix;
stringLen++;
}
//printf("stringLen = %d\n", stringLen);
temp_dataIn = dataIn;
do{
*(bffr+stringLen-1) = (temp_dataIn%radix)+'0';
temp_dataIn = (int) temp_dataIn / radix;
}while(stringLen--);}
and this is example:
char buffer[33];
int main(){
my_utoa(54321, buffer, 10);
printf(buffer);
printf("\n");
my_utoa(13579, buffer, 10);
printf(buffer);
printf("\n");
}

void itos(int value, char* str, size_t size) {
snprintf(str, size, "%d", value);
}
..works with call by reference. Use it like this e.g.:
int someIntToParse;
char resultingString[length(someIntToParse)];
itos(someIntToParse, resultingString, length(someIntToParse));
now resultingString will hold your C-'string'.

char string[something];
sprintf(string, "%d", 42);

Similar implementation to Ahmad Sirojuddin but slightly different semantics. From a security perspective, any time a function writes into a string buffer, the function should really "know" the size of the buffer and refuse to write past the end of it. I would guess its a part of the reason you can't find itoa anymore.
Also, the following implementation avoids performing the module/devide operation twice.
char *u32todec( uint32_t value,
char *buf,
int size)
{
if(size > 1){
int i=size-1, offset, bytes;
buf[i--]='\0';
do{
buf[i--]=(value % 10)+'0';
value = value/10;
}while((value > 0) && (i>=0));
offset=i+1;
if(offset > 0){
bytes=size-i-1;
for(i=0;i<bytes;i++)
buf[i]=buf[i+offset];
}
return buf;
}else
return NULL;
}
The following code both tests the above code and demonstrates its correctness:
int main(void)
{
uint64_t acc;
uint32_t inc;
char buf[16];
size_t bufsize;
for(acc=0, inc=7; acc<0x100000000; acc+=inc){
printf("%u: ", (uint32_t)acc);
for(bufsize=17; bufsize>0; bufsize/=2){
if(NULL != u32todec((uint32_t)acc, buf, bufsize))
printf("%s ", buf);
}
printf("\n");
if(acc/inc > 9)
inc*=7;
}
return 0;
}

Like Edwin suggested, use snprintf:
#include <stdio.h>
int main(int argc, const char *argv[])
{
int n = 1234;
char buf[10];
snprintf(buf, 10, "%d", n);
printf("%s\n", buf);
return 0;
}

If you really want to use itoa, you need to include the standard library header.
#include <stdlib.h>
I also believe that if you're on Windows (using MSVC), then itoa is actually _itoa.
See http://msdn.microsoft.com/en-us/library/yakksftt(v=VS.100).aspx
Then again, since you're getting a message from collect2, you're likely running GCC on *nix.

see this example
#include <stdlib.h> // for itoa() call
#include <stdio.h>
int main() {
int num = 145;
char buf[5];
// convert 123 to string [buf]
itoa(num, buf, 10);
// print our string
printf("%s\n", buf);
return 0;
}
see this link having other examples.

itoa() function is not defined in ANSI-C, so not implemented by default for some platforms (Reference Link).
s(n)printf() functions are easiest replacement of itoa(). However itoa (integer to ascii) function can be used as a better overall solution of integer to ascii conversion problem.
itoa() is also better than s(n)printf() as performance depending on the implementation. A reduced itoa (support only 10 radix) implementation as an example: Reference Link
Another complete itoa() implementation is below (Reference Link):
#include <stdbool.h>
#include <string.h>
// A utility function to reverse a string
char *reverse(char *str)
{
char *p1, *p2;
if (! str || ! *str)
return str;
for (p1 = str, p2 = str + strlen(str) - 1; p2 > p1; ++p1, --p2)
{
*p1 ^= *p2;
*p2 ^= *p1;
*p1 ^= *p2;
}
return str;
}
// Implementation of itoa()
char* itoa(int num, char* str, int base)
{
int i = 0;
bool isNegative = false;
/* Handle 0 explicitely, otherwise empty string is printed for 0 */
if (num == 0)
{
str[i++] = '0';
str[i] = '\0';
return str;
}
// In standard itoa(), negative numbers are handled only with
// base 10. Otherwise numbers are considered unsigned.
if (num < 0 && base == 10)
{
isNegative = true;
num = -num;
}
// Process individual digits
while (num != 0)
{
int rem = num % base;
str[i++] = (rem > 9)? (rem-10) + 'a' : rem + '0';
num = num/base;
}
// If number is negative, append '-'
if (isNegative)
str[i++] = '-';
str[i] = '\0'; // Append string terminator
// Reverse the string
reverse(str);
return str;
}
Another complete itoa() implementatiton: Reference Link
An itoa() usage example below (Reference Link):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int a=54325;
char buffer[20];
itoa(a,buffer,2); // here 2 means binary
printf("Binary value = %s\n", buffer);
itoa(a,buffer,10); // here 10 means decimal
printf("Decimal value = %s\n", buffer);
itoa(a,buffer,16); // here 16 means Hexadecimal
printf("Hexadecimal value = %s\n", buffer);
return 0;
}

if(InNumber == 0)
{
return TEXT("0");
}
const int32 CharsBufferSize = 64; // enought for int128 type
TCHAR ResultChars[CharsBufferSize];
int32 Number = InNumber;
// Defines Decreasing/Ascending ten-Digits to determine each digit in negative and positive numbers.
const TCHAR* DigitalChars = TEXT("9876543210123456789");
constexpr int32 ZeroCharIndex = 9; // Position of the ZERO character from the DigitalChars.
constexpr int32 Base = 10; // base system of the number.
// Convert each digit of the number to a digital char from the top down.
int32 CharIndex = CharsBufferSize - 1;
for(; Number != 0 && CharIndex > INDEX_NONE; --CharIndex)
{
const int32 CharToInsert = ZeroCharIndex + (Number % Base);
ResultChars[CharIndex] = DigitalChars[CharToInsert];
Number /= Base;
}
// Insert sign if is negative number to left of the digital chars.
if(InNumber < 0 && CharIndex > INDEX_NONE)
{
ResultChars[CharIndex] = L'-';
}
else
{
// return to the first digital char if is unsigned number.
++CharIndex;
}
// Get number of the converted chars and construct string to return.
const int32 ResultSize = CharsBufferSize - CharIndex;
return TString{&ResultChars[CharIndex], ResultSize};

Related

How to replace the last index of a string using C language?

The code below tries to increment the last index in a string, eg: if label = "1_1_9", find_next_label (label ) will return "1_1_10".
This works. However, I also want to alter the original label, increment it as well. eg: if label = "1_1_9", find_next_label(label) will return "1_1_10" and during this procedure, label also becomes "1_1_10".
This code below is unable to do this. The result from main() function shows that label is still "1_1_9".
Could anyone help find where the problem is?
char * find_next_lable(char * label)
{
int length = strlen(label);
char * last_index = label + length - 1;
int num = atoi(last_index);
num = num + 1;
char * next_lable = malloc(sizeof(label));
strncpy(next_label, label, length-1);
*(next_label + length - 1) = '\0';
sprintf(next_label, "%s%d", next_label, num);
label = next_label;
return label;
}
int main()
{
char * s = malloc(6);
strcpy(s, "1_1_9");
char * n = find_next_label(s);
printf("%s\n", s);
printf("%s\n", n);
return 0;
}
The last_index() and atoi() code block assumes that the final number is only one digit long; clearly this is not very general. You could search for the last underscore instead, and convert a number from the character following that. Use strrchr() to look for the last underscore.
Also you must think a lot about buffer sizes and overruns, you should probably make the function accept the available buffer size as an additional argument especially if you want to modify the input. If you want that, there's of course no point in allocating additional space either, just return the input.
If you don't need to create a new string you can just do:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LEN 20
int main()
{
char *s = malloc(MAX_LEN); /* You must have enough memory if the number of chars grows! */
char *n;
int i;
strcpy(s, "1_1_9");
printf("%s\n", s);
n = strrchr(s, '_'); /* find the last '_' */
n++; /* and move to the number */
i = atoi(n);
sprintf(n, "%d", i+1); /* write the new value instead of the old one */
printf("%s\n", s);
free(s);
return 0;
}
else you can have the function:
char * find_next_lable(char *label)
{
char *n, *next_lable = malloc(sizeof(MAX_LEN));
int i;
strcpy(next_lable, label);
n = strrchr(next_lable, '_');
n++;
i = atoi(n);
sprintf(n, "%d", i+1);
return next_lable;
}
The result from main() function shows that lable is still 1_1_9.
That's because you are not changing the dynamically allocated array pointed to by s in main. Instead, you allocate new memory in the function find_next_lable. Also,
sprintf(next_lable, "%s%d", next_lable, num);
won't work since %s conversion specifier means that sprintf will read from the buffer pointed to by next_lable till and including the terminating null byte.
You must allocate enough memory so as to contain the incremented integer part.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// make sure MAX is large enough to
// contain the modified string
#define MAX 20
void find_next_lable(char *label);
int main(void)
{
char *s = malloc(MAX);
strcpy(s, "1_1_90");
printf("%s\n", s);
find_next_lable(s);
printf("%s\n", s); // prints 1_1_91
free(s);
return 0;
}
void find_next_lable(char *label)
{
// strrchr returns a pointer to the last
// occurrence of the character _ in label
char *last_index = strrchr(label, '_');
if(last_index == NULL)
{
last_index = label;
}
else
{
last_index++;
}
int num = atoi(last_index);
num = num + 1;
sprintf(last_index, "%d", num);
}

sprintf prepends extra character

I decided to write a binary converter, the code is small and simple , it takes an integer and is supposed to output a char* with the resulting binary string.
The issue here seems to be that the last sprintf always seems to double the last prepended character.
For example if the answer is meant to be 1001001 it will print 11001001 or if it is supposed to be -10 it prints --10, the latter being particularly peculiar as that one isn't even in a loop.
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
void bin_string( char** buffer ,int num ){
bool neg = false;
if ( num < 0 ){
neg = true;
num = ~num+1;
}
if( num == 0 )
sprintf( *buffer, "%d", 0 );
while( num > 0 ){
int rem = num%2;
sprintf( *buffer, "%d%s", rem, *buffer );
printf("iteration: %s rem: %d\n", *buffer, rem );
num = num/2;
}
if( neg )
sprintf( *buffer, "-%s", *buffer );
}
int main( int argc, char** argv ){
char* a = malloc( sizeof(char)*64 );
bin_string( &a, 73 );
printf("Result %s\n",a ); /* output is 11001001 but should be 1001001*/
return 0;
}
The declaration of sprintf() in C99 and beyond is:
int sprintf(char *restrict s, const char *restrict format, ...);
You are violating the restrict part of that declaration. You are using, for example:
sprintf(*buffer, "-%s", *buffer);
This is trying to modify the buffer in situ, and is undefined behaviour. You're lucky you're getting such nearly sane results — or maybe unlucky. You can't use the target buffer in the trailing arguments to the call to sprintf().
I suppose sprintf is just not smart enough to insert buffer into itself. You probably need to have two buffers and swap them.
The answer is that you're invoking undefined behavior when you're sprintf()ing *buffer to itself. What you should do instead is something like:
void bit_string(char *buf, int n)
{
int nbits = sizeof(n) * CHAR_BIT;
int i;
for (i = 0; i < nbits; i++) {
buf[i] = '0' + ((n >> (nbits - i - 1)) & 1);
}
buf[nbits] = 0;
}
(Yes, I've also taken care of efficiency, readability and portability for you - you're welcome.)

String (array) capacity via pointer

I am tring to create a sub-routine that inserts a string into another string. I want to check that the host string is going to have enough capacity to hold all the characters and if not return an error integer. This requires using something like sizeof but that can be called using a pointer. My code is below and I would be very gateful for any help.
#include<stdio.h>
#include<conio.h>
//#include "string.h"
int string_into_string(char* host_string, char* guest_string, int insertion_point);
int main(void) {
char string_one[21] = "Hello mother"; //12 characters
char string_two[21] = "dearest "; //8 characters
int c;
c = string_into_string(string_one, string_two, 6);
printf("Sub-routine string_into_string returned %d and creates the string: %s\n", c, string_one);
getch();
return 0;
}
int string_into_string(char* host_string, char* guest_string, int insertion_point) {
int i, starting_length_of_host_string;
//check host_string is long enough
if(strlen(host_string) + strlen(guest_string) >= sizeof(host_string) + 1) {
//host_string is too short
sprintf(host_string, "String too short(%d)!", sizeof(host_string));
return -1;
}
starting_length_of_host_string = strlen(host_string);
for(i = starting_length_of_host_string; i >= insertion_point; i--) { //make room
host_string[i + strlen(guest_string)] = host_string[i];
}
//i++;
//host_string[i] = '\0';
for(i = 1; i <= strlen(guest_string); i++) { //insert
host_string[i + insertion_point - 1] = guest_string[i - 1];
}
i = strlen(guest_string) + starting_length_of_host_string;
host_string[i] = '\0';
return strlen(host_string);
}
C does not allow you to pass arrays as function arguments, so all arrays of type T[N] decay to pointers of type T*. You must pass the size information manually. However, you can use sizeof at the call site to determine the size of an array:
int string_into_string(char * dst, size_t dstlen, char const * src, size_t srclen, size_t offset, size_t len);
char string_one[21] = "Hello mother";
char string_two[21] = "dearest ";
string_into_string(string_one, sizeof string_one, // gives 21
string_two, strlen(string_two), // gives 8
6, strlen(string_two));
If you are creating dynamic arrays with malloc, you have to store the size information somewhere separately anyway, so this idiom will still fit.
(Beware that sizeof(T[N]) == N * sizeof(T), and I've used the fact that sizeof(char) == 1 to simplify the code.)
This code needs a whole lot more error handling but should do what you need without needing any obscure loops. To speed it up, you could also pass the size of the source string as parameter, so the function does not need to calculate it in runtime.
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
signed int string_into_string (char* dest_buf,
int dest_size,
const char* source_str,
int insert_index)
{
int source_str_size;
char* dest_buf_backup;
if (insert_index >= dest_size) // sanity check of parameters
{
return -1;
}
// save data from the original buffer into temporary backup buffer
dest_buf_backup = malloc (dest_size - insert_index);
memcpy (dest_buf_backup,
&dest_buf[insert_index],
dest_size - insert_index);
source_str_size = strlen(source_str);
// copy new data into the destination buffer
strncpy (&dest_buf[insert_index],
source_str,
source_str_size);
// restore old data at the end
strcpy(&dest_buf[insert_index + source_str_size],
dest_buf_backup);
// delete temporary buffer
free(dest_buf_backup);
}
int main()
{
char string_one[21] = "Hello mother"; //12 characters
char string_two[21] = "dearest "; //8 characters
(void) string_into_string (string_one,
sizeof(string_one),
string_two,
6);
puts(string_one);
return 0;
}
I tried using a macro and changing string_into_string to include the requirement for a size argument, but I still strike out when I call the function from within another function. I tried using the following Macro:
#define STRING_INTO_STRING( a, b, c) (string_into_string2(a, sizeof(a), b, c))
The other function which causes failure is below. This fails because string has already become the pointer and therefore has size 4:
int string_replace(char* string, char* string_remove, char* string_add) {
int start_point;
int c;
start_point = string_find_and_remove(string, string_remove);
if(start_point < 0) {
printf("string not found: %s\n ABORTING!\n", string_remove);
while(1);
}
c = STRING_INTO_STRING(string, string_add, start_point);
return c;
}
Looks like this function will have to proceed at risk. looking at strcat it also proceeds at risk, in that it doesn't check that the string you are appending to is large enough to hold its intended contents (perhaps for the very same reason).
Thanks for everyone's help.

Why is substring not part of the C standard library?

I know C is purposefully bare-bones, but I'm curious as to why something as commonplace as a substring function is not included in <string.h>.
Is it that there is not one "right enough" way to do it? Too many domain specific requirements? Can anyone shed any light?
BTW, this is the substring function I came up with after a bit of research.
Edit: I made a few updates based on comments.
void substr (char *outStr, const char *inpStr, int startPos, size_t strLen) {
/* Cannot do anything with NULL. */
if (inpStr == NULL || outStr == NULL) return;
size_t len = strlen (inpStr);
/* All negative positions to go from end, and cannot
start before start of string, force to start. */
if (startPos < 0) {
startPos = len + startPos;
}
if (startPos < 0) {
startPos = 0;
}
/* Force negative lengths to zero and cannot
start after end of string, force to end. */
if ((size_t)startPos > len) {
startPos = len;
}
len = strlen (&inpStr[startPos]);
/* Adjust length if source string too short. */
if (strLen > len) {
strLen = len;
}
/* Copy string section */
memcpy(outStr, inpStr+startPos, strLen);
outStr[strLen] = '\0';
}
Edit: Based on a comment from r I also came up with this one liner. You're on your own for checks though!
#define substr(dest, src, startPos, strLen) snprintf(dest, BUFF_SIZE, "%.*s", strLen, src+startPos)
Basic standard library functions don't burden themselves with excessive expensive safety checks, leaving them to the user. Most of the safety checks you carry out in your implementation are of expensive kind: totally unacceptable in such a basic library function. This is C, not Java.
Once you get some checks out of the picture, the "substrung" function boils down to ordinary strlcpy. I.e ignoring the safety check on startPos, all you need to do is
char *substr(const char *inpStr, char *outStr, size_t startPos, size_t strLen) {
strlcpy(outStr, inpStr + startPos, strLen);
return outStr;
}
While strlcpy is not a part of the standard library, but it can be crudely replaced by a [misused] strncpy. Again, ignoring the safety check on startPos, all you need to do is
char *substr(const char *inpStr, char *outStr, size_t startPos, size_t strLen) {
strncpy(outStr, inpStr + startPos, strLen);
outStr[strLen] = '\0';
return outStr;
}
Ironically, in your code strncpy is misused in the very same way. On top of that, many of your safety checks are the direct consequence of your choosing a signed type (int) to represent indices, while proper type would be an unsigned one (size_t).
Perhaps because it's a one-liner:
snprintf(dest, dest_size, "%.*s", sub_len, src+sub_start);
You DO have strcpy and strncpy. Aren't enough for you? With strcpy you can simulate the substring from character to end, with strncpy you can simulate the substring from character for a number of characters (you only need to remember to add the \0 at the end of the string). strncpy is even better than the C# equivalent, because you can overshoot the length of the substring and it won't throw an error (if you have allocated enough space in dest, you can do strncpy(dest, src, 1000) even if src is long 1. In C# you can't.)
As written in the comment, you can even use memcpy, but remember to always add a \0 at the end of the string, and you must know how many characters you are copying (so you must know exactly the length of the src substring) AND it's a little more complex to use if a day you want to refactor your code to use wchar_t AND it's not type-safe (because it accepts void* instead of char*). All this in exchange for a little more speed over strncpy
In C you have a function that returns a subset of symbols from a string via pointers: strstr.
char *ptr;
char string1[] = "Hello World";
char string2[] = "World";
ptr = strstr(string1, string2)
*ptr will be pointing to the first character occurrence.
BTW you did not write a function but a procedure, ANSI string functions: string.h
Here's a lighter weight version of what you want. Avoids the redundant strlen calls and guarantees null termination on the destination buffer (something strncpy won't do).
void substr(char* pszSrc, int start, int N, char* pszDst, int lenDest)
{
const char* psz = pszSrc + start;
int x = 0;
while ((x < N) && (x < lenDest))
{
char ch = psz[x];
pszDst[x] = ch;
x++;
if (ch == '\0')
{
return;
}
}
// guarantee null termination
if (x > 0)
{
pszDest[x-1] = 0;
}
}
Example:
char *pszLongString = "This is a long string";
char szSub[10];
substr(pszLongString, 0, 4, szSub, 10); // copies "long" into szSub and includes the null char
So while there isn't a formal substring function in C, C++ string classes usually have such a method:
#include <string>
...
std::string str;
std::string strSub;
str = "This is a long string";
strSub = str.substr(10, 4); // "long"
printf("%s\n", strSub.c_str());
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const char* substr(const char *string, size_t from, size_t to);
int main(int argc, char *argv[])
{
char *string = argv[1];
const char *substring = substr(string,6,80);
printf("string is [%s] substring is [%s]\n",string,substring);
return 0;
}
const char* substr(const char *string, size_t from, size_t to)
{
if (to <= from)
return NULL;
if (from >= to)
return NULL;
if (string == NULL)
return NULL;
if (strlen(string) == 0)
return NULL;
if (from < 0)
from = 0;
if (to > strlen(string))
to = strlen(string);
char *substring = malloc(sizeof(char) * ((to-from)+1));
size_t index;
for (index = 0; from < to; from++, index++)
substring[index] = string[from];
substring[index] = '\0';
return substring;
}

Why doesn't my C code snippet work? Simplistic version does. Passing args without VA_ARGS for unsigned long long

Basically I'm writing a printf function for an dedicated system so I want to pass an optional number of arguments without using VA_ARGS macros. I knocked up a simple example and this block of code works:
#include <stdio.h>
void func(int i, ...);
int main(int argc, char *argv);
int main(int argc, char *argv) {
unsigned long long f = 6799000015ULL;
unsigned long long *g;
//g points to f
g = &f;
printf("natural: %llu in hex: %llX address: %x\n", *g, *g, g);
//put pointer onto stack
func(6, g, g);
return 0;
}
void func(int i, ...) {
unsigned long long *f;
//pop value off
f = *(&i + 1);
printf("address: %x natural: %llu in hex: %llX\n", f, *f, *f);
}
However the larger example I'm trying to transfer this to doesn't work.
(in the main function):
unsigned long long f = 6799000015ULL;
unsigned long long *g;
g = &f;
kprintf("ull test: 1=%U 2=%X 3=%x 4= 5=\n", g, g, g);
(my dodgy printf function that I'm having trouble with. It maybe worth pointing out
this code DOES work with ints, char strings or anyother % flags which are passed by
value and not pointer. The only difference between what did work and the unsigned
long longs is one is bigger, so I pass by value instead to ensure I don't increment
the &format+ args part wrongly. Does that make sense?)
void kprintf(char *format, ...)
{
char buffer[KPRINTF_BUFFER_SIZE];
int bpos = 0; /* position to write to in buffer */
int fpos = 0; /* position of char to print in format string */
char ch; /* current character being processed*/
/*
* We have a variable number of paramters so we
* have to increment from the position of the format
* argument.
*/
int arg_offset = 1;
/*
* Think this through Phill. &format = address of format on stack.
* &(format + 1) = address of argument after format on stack.
* void *p = &(format + arg_offset);
* kprintf("xxx %i %s", 32, "hello");
* memory would look like = [ 3, 32, 5, "xxx", 32, "hello" ]
* get to 32 via p = &(format + 1); (int)p (because the int is copied, not a pointer)
* get to hello via p = &(format + 2); (char*)p;
*/
void *arg;
unsigned long long *llu;
arg = (void*) (&format + arg_offset);
llu = (unsigned long long*) *(&format + arg_offset);
while (1)
{
ch = format[fpos++];
if (ch == '\0')
break;
if (ch != '%')
buffer[bpos++] = ch;
else
{
ch = format[fpos++];
if (ch == 's')
bpos += strcpy(&buffer[bpos], KPRINTF_BUFFER_SIZE - bpos, (char*)arg);
else if (ch == '%')
buffer[bpos++] = '%';
else if (ch == 'i')
bpos += int_to_str(&buffer[bpos], KPRINTF_BUFFER_SIZE - bpos, *((int*)arg));
else if (ch == 'x')
bpos += int_to_hex_str(&buffer[bpos], KPRINTF_BUFFER_SIZE - bpos, *((int*)arg));
else if (ch == 'o')
bpos += int_to_oct_str(&buffer[bpos], KPRINTF_BUFFER_SIZE - bpos, *((int*)arg));
else if (ch == 'X') {
//arg is expected to be a pointer we need to further dereference.
bpos += unsigned_long_long_to_hex(&buffer[bpos], KPRINTF_BUFFER_SIZE - bpos, *llu);
} else if (ch == 'U') {
bpos += unsigned_long_long_to_str(&buffer[bpos], KPRINTF_BUFFER_SIZE - bpos, *llu);
} else
{
puts("invalid char ");
putch(ch);
puts(" passed to kprintf\n");
}
arg_offset++;
arg = (void *)(&format + arg_offset);
llu = (unsigned long long*) *(&format + arg_offset);
}
}
buffer[bpos] = '\0';
puts(buffer);
}
(and the unsigned long long functions it goes on to call):
int unsigned_long_long_to_hex(char *buffer, int max_size, unsigned long long number)
{
return ull_number_to_str(buffer, max_size, number, BASE_HEX);
}
int unsigned_long_long_to_str(char *buffer, int max_size, unsigned long long number) {
return ull_number_to_str(buffer, max_size, number, BASE_DECIMAL);
}
int ull_number_to_str(char *buffer, int max_size, unsigned long long number, int base) {
int bufpos = 0;
unsigned int lo_byte = (unsigned int) number;
unsigned int hi_byte = (unsigned int) (number >> 32);
bufpos = number_to_str(buffer, max_size, lo_byte, base);
bufpos += number_to_str(buffer + bufpos, max_size, hi_byte, base);
return bufpos;
}
#define NUMERIC_BUFF_SIZE (11 * (ADDRESS_SIZE / 32))
int number_to_str(char *buffer, int max_size, int number, int base)
{
char *char_map = "0123456789ABCDEF";
int remain = 0;
char buff_stack[NUMERIC_BUFF_SIZE];
int stk_pnt = 0;
int bpos = 0;
/* with this method of parsing, the digits come out backwards */
do
{
if (stk_pnt > NUMERIC_BUFF_SIZE)
{
puts("Number has too many digits to be printed. Increasse NUMBERIC_BUFF_SIZE\n");
return 0;
}
remain = number % base;
number = number / base;
buff_stack[stk_pnt++] = char_map[remain];
} while (number > 0);
/* before writing...ensure we have enough room */
if (stk_pnt > max_size)
{
//error. do something?
puts("number_to_str passed number with too many digits to go into buffer\n");
//printf("error. stk_pnt > max_size (%d > %d)\n", stk_pnt, max_size);
return 0;
}
/* reorder */
while (stk_pnt > 0)
buffer[bpos++] = buff_stack[--stk_pnt];
return bpos;
}
Sorry guys, I can't see what I've done wrong. I appreciate this is a "wall of code" type scenario but hopefully someone can see what I've done wrong. I appreciate you probably dislike not using VA_ARGS but I don't understand why this technique shouldn't just work? And also, I'm linking with -nostdlib too. If someone can help I'd really appreciate it. Also, this isn't meant to be production quality code so if I lack some C fundamentals feel free to be constructive about it :-)
It's a bad idea to code this way. Use stdarg.h.
On the off chance (I presume this based on the name kprintf) that you're working on a hobby kernel or embedded project and looking to avoid using standard libraries, I recommend at least writing your own (architecture and compiler specific) set of stdarg macros that conform to the well-known interfaces and code against that. That way your code doesn't look like such a WTF by dereferencing past the address of the last argument.
You can make a va_list type that stores the last-known address, and your va_arg macro could appropriately align the sizeof of the type it's passed and advance the pointer accordingly. For most conventions I have worked on for x86, every type is promoted to 32 bits...
You have to read on the calling conventions for your platform, i.e. how on your target processor/OS function arguments are passed, and how registers are saved. Not all parameters are passed on stack. Depending on number of parameters and their types, many complex situations can arise.
I should add: if you want to manipulate the stack by hand as you are doing above, you need to do it in assembler, not in C. The C language follows a defined standard, and what you are doing above it not legal code (i.e., its meaning is not well-defined). As such, the compiler is allowed to do anything it wants with it, such as optimize it in weird ways unsuitable to your needs.

Resources