I cannot figure out how to assign an integer value to an array of string in C, assuming this is possible of course! I would like to change some elements from a char ** into different values and then re-assign them to their initial position into the array.
Practically, I have two element, "Mon" and "Aug", which I would like to become "01" and "08"...
So I tried something but unsuccessfully:
char *time_array[7] = {"45", "55", "11", "Mon", "29", "Aug", "2022"};
uint32_t day2convert = 1;
uint32_t month2convert = 8;
*time_array[3] = &day2convert; // not even with a cast (char) which gave same results
*time_array[5] = &month2convert;
for (j = 0; j < 7; ++j)
printf("after conversion : %s\n", time_array[j]);
/*
Results for the two elements are different at each main call, as I'm randomly picking values present in different memory allocations (maybe). Es. don, hug; 4on 8ug, ton xug...
Moreover i tried also with strcpy function as:
strcpy(time_array[3], &day2convert); //also with a cast (char)(&day2convert)
but still I retrieve a void string.
So, gently, I'm asking if there could be a way to do what I want in principle, but also asking for some explanations about array of string. Because what I learnt is that a char ** is an array of pointer so it should be right to assign a value to it as *array[ii] = &var (??? in search of confirmation)
... how to assign an integer value to an array of string (?)
time_array[] is not an array of strings. It is an array of pointers to strings. This distinction is important as string lifetimes need careful consideration. Here, the strings pointed to by time_array[i] are not certainty modifiable.
char *time_array[7] = {"45", "55", "11", "Mon", "29", "Aug", "2022"};
We could change the pointer by re-assigning a compound literal. It is easy to form a an integer like string with single digit values like 1-7. Yet the lifetime of the compound literal only lasts to the end of the block of code.
int day_index = 1;
time_array[3] = (char[3]){ '0', '0' + day_index, '\0'}; // , '\0' optional as rest is zero filled
// good for a while
Or perhaps make time_array[] an array of strings and then re-assign the indexed string.
char time_array[7][5] = {"45", "55", "11", "Mon", "29", "Aug", "2022"};
int day_index = 1;
strcpy(time_array[3], (char[3]){ '0', '0' + day_index });
// or
snprintf(time_array[3], sizeof time_array[3], "%02d", day_index);
// good indefinitely
You will want to convert each of the 7 day names to a number.
I wrote this "hash function" for quickly getting back 1 to 7 for "Sun" to "Saturday" (it only needs the first two characters of the name, and is case insensitive).
It will return false positives, but allows you to test the string you have against only one possible day name. If the function returns 2, then the string can be checked as a variation of "Tu", "TUE" "Tuesday". You don't have to examine the other six day names as possibilities.
Return of 0 indicates it is not a weekday name.
// Convert a day name (or 3 letter abbrev.) to index (1-7, Sun=1). Beware false positives!
int wkdayOrd( char cp[] ) { return "65013427"[*cp/2 + ~cp[1] & 0x7] & 0x7; }
If you carefully replace 2 with 1, 3 with 2, 4 with 3, etc. in the quoted string, it will return Mon=1 and Sun=7 as you desire...
And for month names "Jan-December" (3 letters necessary) into 1 to 12. Again, this is a "hash function" and other words can/will result in "false positives" that must be confirmed against only one proper month name string.
int monthOrd( char cp[] ) { return "DIE#CB#LJF#HAG#K"[ cp[1]/4&7 ^ cp[2]*2 &0xF ] &0xF; }
Give it a play and let us know if it helps you get where you want to be.
EDIT:
Demonstrating functionality, here is an example that satisfies the OP question. The LUT values have been adjusted so that "Mon(day)" is day 1.
This does the conversion (trusting the source string) without invoking sprintf(). Here, printf() is used for 'diagnostic testing' only.
static int monthOrd( char cp[] ) { return "DIE#CB#LJF#HAG#K"[ cp[1]/4&7 ^ cp[2]*2 &0xF ] &0xF; }
static int wkdayOrd( char cp[] ) { return "54072316"[*cp/2 + ~cp[1] & 0x7] & 0x7; }
static char *toDigitStr( int val ) { // multiple execution overwrites result
static char buf[3];
buf[0] = (char)('0' + val/10);
buf[1] = (char)('0' + val%10);
buf[2] = '\0';
return buf;
}
int main() {
char *dayName = "Mon";
printf( "%s ==> %s\n", dayName, toDigitStr( wkdayOrd( dayName ) ) );
char *mthName = "Aug";
printf( "%s ==> %s\n", mthName, toDigitStr( monthOrd( mthName ) ) );
mthName = "DECEMBER";
printf( "%s ==> %s\n", mthName, toDigitStr( monthOrd( mthName ) ) );
return 0;
}
Output:
Mon ==> 01
Aug ==> 08
DECEMBER ==> 12
Related
I am trying to make a rock,paper,scissors game using functions in C yet I cant seem to figure out how to assign the string to a char variable after I initialize it. So what the function does is that it generates a random number and compares that number to ether rock paper or scissors. yet I cant assign the the string to a variable which what I want the function to return after its called
int getConsoleChoice()
{
char consolePick;
int randNum;
srand(time(NULL));
randNum = rand() % 3;
if(randNum == 0)
{
char consolePick = "Rock";
}
if(randNum == 1)
{
char consolePick = "Paper";
}
if(randNum == 2)
{
char consolePick = "Scissors";
}
return consolePick;
}
Declare the variable and function return value as char *, which is the type of a string in C.
char *getConsoleChoice()
{
char *consolePick;
int randNum;
srand(time(NULL));
randNum = rand() % 3;
if(randNum == 0)
{
consolePick = "Rock";
}
else if(randNum == 1)
{
consolePick = "Paper";
}
else
{
consolePick = "Scissors";
}
return consolePick;
}
You could also simplify this using an array:
char *getConsoleChoice()
{
char *moves[] = {"Rock", "Paper", "Scissors"};
int randNum;
srand(time(NULL));
randNum = rand() % 3;
return moves[randNum];
}
char represents a single character. A string is a bunch of these stored together somewhere in memory, with a NUL terminating character so we can detect where that string finishes. So, when we have a memory location storing a bunch of values of some type, we use a pointer. It holds a memory address that "points" at the start. The type for a string is char*.
To illustrate, let's draw out some bytes in memory showing a string at some address, let's imagine we have a 32-bit computer and for argument's sake the string begins at the address 0xffa01000:
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
0xffa01000 | R | o | c | k | ~ | ? | ? | ? |
+---+---+---+---+---+---+---+---+
The above is what "Rock" looks like in memory. Each one of those cells is one byte holding a char value. Note that I've used ~ to represent the NUL byte and ? to represent memory we don't care about (because it's not part of the string).
If you want to store a value that points to that string, you use a pointer:
const char* rock = "Rock";
With our example from earlier, the pointer rock now holds the address 0xffa01000 and if we start reading information out of memory from there until we hit the NUL byte, then we will have read the string "Rock".
You can use a pointer like an array. From above, rock[0] will give you the character 'R', rock[1] will give 'o', and so on...
So there's some basics on what strings actually are in C. But back to your game.
With what you're trying to do, the goal is probably to use a value of 0, 1 or 2 to represent a weapon. Because it's just easier to work with. You can always turn that into a string when you need to. Here is an example of how you could do that:
const char* weaponName(int w)
{
static const char* names[] = {
"Rock",
"Paper",
"Scissors"
};
// Note: the defensive programming approach would also range-test this index
// before using it.
return names[w];
}
This function just uses the weapon number as an index to find a string in an array. Think back to earlier... What we have here is just an array of pointers to strings that live somewhere in memory. You don't need to care where.
Note that I've also used const. Don't worry too much about that, but what this does is prevents me from accidentally modifying those strings. Modifying string literals is not allowed, and so we store them as constants to make the compiler complain if we try to modify them.
As for static, well that's another topic but it basically means that one copy of this array will be always in memory, even after the function returns. So I don't need to be concerned about my strings somehow not existing after the function returns. They probably wouldn't disappear anyway, but this makes sure.
Anyway, back to how to use this, it's quite simple. Your random weapon roll becomes a one-line function, and getting the string representation is just a call to the function we made earlier.
Here's some code that will roll a weapon 10 times and print out what it got:
int randomWeapon()
{
return rand() % 3;
}
int main()
{
// Seed the random generator -- you should only do this once!
srand(time(0));
for (int i = 0; i < 10; ++i)
{
int weapon = randomWeapon();
printf("%s\n", weaponName(weapon));
}
}
DISCLAIMER: it's just a piece of the whole algorithm, but since I encountered a lot of errors, I've decided to divide and conquer, therefore, I start with the following code. (goal of the following code: create a string with the remainders of the division by 10 (n%10). for now, it's not reversed, I haven't done it yet, because I wanted to check this code first).
(i'm working in C, in visual studio environment).
I have to implement a function like atoi (that works from a string to a number), but I want to do the opposite (from a number to a string). but I have a problem:
the debugger pointed out that in the lines with the malloc, I should have initialized the string first (initialization requires a brace-enclosed initializer list),
but I have done it, I have initialized the string to a constant (in the 2nd line, I've written "this is the test seed")(because I need to work with a string, so I initialized, and then I malloc it to write the values of (unsigned int n) ).
this is how my program is supposed to work:
(1) the function takes an unsigned int constant (n),
(2) the function creates a "prototype" of the array (the zero-terminated string),
(3) then, I've created a for-loop without a check condition because I added it inside the loop body,
(4) now, the basic idea is that: each step, the loop uses the i to allocate 1 sizeof(char) (so 1 position) to store the i-th remainder of the n/10 division. n takes different values every steps ( n/=10; // so n assumes the value of the division). and if n/10 is equal to zero, that means I have reached the end of the loop because each remainder is in the string). Therefore, I put a break statement, in order to go outside the for-loop.
finally, the function is supposed to return the pointer to the 0-th position of the string.
so, to sum up: my main question is:
why do I have " "s": initialization requires a brace-enclosed initializer list"? (debugger repeated it twice). that's not how string is supposed to be initialized (with curly braces "{}"). String is initialized with " " instead, am I wrong?
char* convert(unsigned int n) {
char s[] = "this is the test seed";
for (unsigned int i = 0; ; i++) {
if (i == 0) {
char s[] = malloc (1 * sizeof(char));
}
if (i != 0) {
char s[] = malloc(i * sizeof(char));
}
if ((n / 10) == 0) {
break;
}
s[i] = n % 10;
n /= 10;
}
return s;
}
char s[]is an array, and therefore needs a brace-enclosed initializer list (or a character string literal). In the C standard, see section 6.7.8 (with 6.7.8.14 being the additional special case of a literal string for an array of character type). char s[] = malloc(...); is neither a brace-enclosed initializer list or a literal string, and the compiler is correctly reporting that as an error.
The reason for this, is that char s[] = ...; declares an array, which means that the compiler needs to know the length of the array at compile-time.
Perhaps you want char *s = malloc(...) instead, since scalars (for example, pointers) can be initialized with an assignment statement (see section 6.7.8.11).
Unrelated to your actual question, the code you've written is flawed, since you're returning the value of a local array (the first s). To avoid memory problems when you're coding, avoid mixing stack-allocated memory, statically allocated strings (eg: literal strings), and malloc-ed memory. If you mix these together, you'll never know what you can or can't do with the memory (for example, you won't be sure if you need to free the memory or not).
A complete working example:
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
char *convert(unsigned n) {
// Count digits of n (including edge-case when n=0).
int len = 0;
for (unsigned m=n; len == 0 || m; m /= 10) {
++len;
}
// Allocate and null-terminate the string.
char *s = malloc(len+1);
if (!s) return s;
s[len] = '\0';
// Assign digits to our memory, lowest on the right.
while (len > 0) {
s[--len] = '0' + n % 10;
n /= 10;
}
return s;
}
int main(int argc, char **argv) {
unsigned examples[] = {0, 1, 3, 9, 10, 100, 123, 1000000, 44465656, UINT_MAX};
for (int i = 0; i < sizeof(examples) / sizeof(*examples); ++i) {
char *s = convert(examples[i]);
if (!s) {
return 2;
}
printf("example %d: %u -> %s\n", i, examples[i], s);
free(s);
}
return 0;
}
It can be run like this (note the very useful -fsanitize options, which are invaluable especially if you're beginning programming in C).
$ gcc -fsanitize=address -fsanitize=leak -fsanitize=undefined -o convert -Wall convert.c && ./convert
example 0: 0 -> 0
example 1: 1 -> 1
example 2: 3 -> 3
example 3: 9 -> 9
example 4: 10 -> 10
example 5: 100 -> 100
example 6: 123 -> 123
example 7: 1000000 -> 1000000
example 8: 44465656 -> 44465656
example 9: 4294967295 -> 4294967295
So I have this function :
char *lookUpPageTable(char **array, int VPN)
{
if (array[VPN][0] == '1')
{
/*char **pageNumber = (char **)malloc(sizeof(char*)* 128);
for (int i = 0; i < strlen(array); i++)
{
pageNumber[i] = array[VPN][i];
}*/
return array[VPN]; //this returns the whole number which I dont want
}
else
{
return "Page Fault";
}
}
The array I'm passing in as a parameter holds a list of numbers in the form 1 123456 where the first number is either a 1 or a 0 and the second number is a random number. This function checks the first number at index VPN in the array. If it is a zero, it should return a "Page Fault" string. If it is a 1, then the function should return the number after the 1.
For example, if i called lookUpPageTable(array, index)
The method should see if array[index][0] == '1'
If it does then return the remaining numbers at array[index]
else
return "page fault"
array[VPN] is the VPN-th element of the array, which happens to be a pointer to the string "1 123456" as you say. If you return array[VPN] + 1, for example, it would be a pointer to the string " 123456".
So you may return array[VPN] + 2, and you will obtain a pointer to the string "123456" as desired.
Note, however, that I am relying on your guarantee that the string's contents are indeed something of the form "1 123456", and I would recommend that your code should also verify that the string really is of that form.
I am new to C, so forgive me if this question is trivial. I am trying to reverse a string, in
my case the letters a,b,c,d. I place the characters in a char* array, and declare a buffer
which will hold the characters in the opposite order, d,c,b,a. I achieve this result using
pointer arithmetic, but to my understanding each element in a char* array is 4 bytes, so when I do the following: buffer[i] = *(char**)letters + 4; I am supposed to be pointing at the
second element in the array. Instead of pointing to the second element, it points to the third. After further examination I figured that if I increment the base pointer by two
each time I would get the desired results. Does this mean that each element in the array
is two bytes instead of 4? Here is the rest of my code:
#include <stdio.h>
int main(void)
{
char *letters[] = {"a","b","c","d"};
char *buffer[4];
int i, add = 6;
for( i = 0 ; i < 4 ; i++ )
{
buffer[i] = *(char**)letters + add;
add -= 2;
}
printf("The alphabet: ");
for(i = 0; i < 4; i++)
{
printf("%s",letters[i]);
}
printf("\n");
printf("The alphabet in reverse: ");
for(i = 0; i < 4; i++)
{
printf("%s",buffer[i]);
}
printf("\n");
}
You're not making an array of characters: you're making an array of character strings -- i.e., an array of pointers to arrays of characters. I am not going to rewrite the whole program for you of course, but I'll start out with two alternative possible correct declarations for your main data structure:
char letters[] = {'a','b','c','d, 0};
char * letters = "abcd";
Either of these declares an array of five characters: a, b, c, d followed by 0, the traditional ending for a character string in C.
Another thing: rather than making assumptions about the size of things, use the language to tell you. For instance:
char *my_array[] = { "foo" , "bar" , "baz" , "bat" , } ;
// the size of an element of my_array
size_t my_array_element_size = sizeof(my_array[0]) ;
size_t alt_element_size = size(*my_array) ; // arrays are pointers under the hood
// the number of elements in my_array
size_t my_array_element_cnt = sizeof(my_array) / sizeof(*myarray ;
// the size of a char
size_t char_size = sizeof(*(my_array[0])) ; // size of a char
Another thing: understand your data structures (as noted above). You talk about chars, but your data structures are talking about strings. Your declarations:
char *letters[] = {"a","b","c","d"};
char *buffer[4];
get parsed as follows:
letters is an array of pointers to char (which happen to be nul-terminated C-style strings), and it's initialized with 4 elements.
Like letters, buffer is an array of 4 pointers to char, but uninitialized.
You are not actually dealing individual chars anywhere, even in the printf() statements: the %s specifier says the argument is a nul-terminated string. Rather, you're dealing with strings (aka pointers to char) and arrays of the same.
An easier way:
#include <stdio.h>
int main(void)
{
char *letters[] = { "a" , "b" , "c" , "d" , } ;
size_t letter_cnt = size(letters)/sizeof(*letters) ;
char *buffer[sizeof(letters)/sizeof(*letters)] ;
for ( int i=0 , j=letter_cnt ; i < letter_cnt ; ++i )
{
buffer[--j] = letters[i] ;
}
printf("The alphabet: ");
for( int i = 0 ; i < letter_cnt ; ++i )
{
printf("%s",letters[i]);
}
printf("\n");
printf("The alphabet in reverse: ");
for( int i=0 ; i < letter_cnt ; i++ )
{
printf("%s",buffer[i]);
}
printf("\n");
}
BTW, is this homework?
This is a case of operator precedence. When you use buffer[i] = *(char**)letters + add;, the * before the cast is performed before the +, making this code equivalent to (*(char**)letters) + add;. The first part is equivalent to the address of the first element in your array, the string "a". Since using string constant automatically adds a null byte, this points to 'a\0'. It happens that the compiler placed all four strings immediately after each other in memory, so if you go past the end of that string you flow into the next. When you add to the pointer, you are moving through this character array: 'a\0b\0c\0d\0'. Notice that each character is 2 bytes after the last. Since this is only true because the compiler placed the 4 strings directly after each other, you should never depend on it (it won't even work if you tried to re-reverse your other string). Instead, you need to put in parentheses to make sure the addition happens before the dereference, and use the 4 byte pointer size. (Of course, as pointed out by Nicholas, you shouldn't assume the size of anything. Use sizeof to get the size of a pointer instead.)
buffer[i] = *((char**)letters + add);
char *letters[] = {"a","b","c","d"};
I think you didn't get the pointer arithmetic correctly. letters is an array of pointers and when incremented by 1 makes to go to next row.
letters + 1 ; // Go to starting location of 2 row, i.e., &"b"
char *letters[] = { "abc" , "def" } ;
(letters + 1) ; // Point to the second row's first element, i.e., &"d"
*((*letters) + 1) ; // Get the second element of the first row. i.e., "b"
I have a character array that contains a phone number of the form: "(xxx)xxx-xxxx xxxx" and need to convert it to something of the form: "xxx-xxx-xxxx" where I would just truncate the extension. My initial pass at the function looks like this:
static void formatPhoneNum( char *phoneNum ) {
unsigned int i;
int numNumbers = 0;
/* Change the closing parenthesis to a dash and truncate at 12 chars. */
for ( i = 0; i < strlen( phoneNum ); i++ ) {
if ( phoneNum[i] == ')' ) {
phoneNum[i] = '-';
}
else if ( i == 13 ) {
phoneNum[i] = '\0';
break;
}
else if ( isdigit( phoneNum[i] ) ) {
numNumbers++;
}
}
/* If the phone number is empty or not a full phone number,
* i.e. just parentheses and dashes, or not 10 numbers
* format it as an emtpy string. */
if ( numNumbers != 10 ) {
strcpy( phoneNum, "" );
}
else {
/* Remove the first parenthesis. */
strcpy( phoneNum, phoneNum + 1 );
}
}
It feels kinda hokey the way I'm removing the leading paren, but I can't just increment the pointer in the function as the calling version's pointer won't get updated. I also feel like I could be "more clever" in general all throughout the function.
Any ideas/pointers?
Since you stated that your input is guaranteed to be in the proper format, how about the following:
static void formatPhoneNum( char *phoneNum )
{
memmove(phoneNum, phoneNum + 1, 12);
phoneNum[3] = '-';
phoneNum[12] = 0;
}
memmove() is guaranteed to work with overlapping buffers
As Pavel said, you can't strcpy a string onto itself. I'm declaring a new variable for clarity, although my approach doesn't use strcpy - with care, you could re-use the original variable. Anyway, if your input is always of the form (xxx) xxx-xxxx xxxx, and your output is always going to be xxx-xxx-xxxx why not just do:
char newPhone[14];
newPhone[0] = phoneNum[1];
newPhone[1] = phoneNum[2];
newPhone[2] = phoneNum[3];
newPhone[3] = '-';
newPhone[4] = phoneNum[6];
newPhone[5] = phoneNum[7];
newPhone[6] = phoneNum[8];
newPhone[7] = '-';
newPhone[8] = phoneNum[10];
newPhone[9] = phoneNum[11];
newPhone[10] = phoneNum[12];
newPhone[11] = phoneNum[13];
newPhone[12] = '\0';
Brute force? Sure, but - if your inputs and outputs are always going to be as you state - it should run efficiently.
Well I guess I'm just too slow. Nothing clever about this over memmove(), but it shows how you can have a loop and still take all those comparisons out of the inside:
char *formatPhoneNum(char *buffer) {
int index = 0;
for( index = 0; index < 12; ++index ) {
buffer[index] = buffer[index + 1];
}
buffer[3] = '-';
buffer[12] = '\0';
return buffer;
}
You may find it helpful if you return the start of the string you modify instead of just void so you can chain commands easier. E.g.,
printf("%s\n", formatPhoneNum(buffer));
For starters, this is wrong:
strcpy( phoneNum, phoneNum + 1 );
because ISO C standard says regarding strcpy:
If copying takes place between objects that overlap, the behavior is undefined.
"objects" here being source and destination char arrays. MSDN concurs with this, by the way, so this won't work properly on at least one popular real-world implementation.
It seems that a simpler approach would be to have the function return a new "proper" value of the pointer (into the same buffer), so it can adjust it by 1 to trim the '('.
Your validation, which simply counts digits, permits formatting such as "1-234567890" or "1234567890-" or even "12345foobar4567890" - this may or may not be a problem, depending on requirements.
When possible (and not performance degrading) I prefer to pass data to functions as consts. In your case I see no reason not to do it, so I'd declare your function as
static void formatPhoneNum(char *dst, const char *src);
or even, returning the length of the new number:
static int formatPhoneNum(char *dst, const char *src);
Then, just copy digits from src to dst inserting dashes at the right places. The caller is responsible to provide space in dst and check the return value: if 12 (dashes included), all ok; otherwise there was an error.
You can return some negative number to indicate possible errors. For example: -1 would indicate that src is not long enough; -2 would indicate a bad format for src, etc...
Do document all the return values!
Oh! And do not forget to NUL terminate dst!
If you are allowed to change the API, you could either accept a char** or return a char *, and improve the time complexity:
static void formatPhoneNum(char **phoneNum) {
(*phoneNum)[4] = '-';
(*phoneNum)[13] = '\0';
(*phoneNum)++;
}
Alternately
static char *formatPhoneNum(char *phoneNum) {
phoneNum[4] = '-';
phoneNum[13] = '\0';
return phoneNum + 1;
}
The advantage is that this will take constant time.