473,397 Members | 1,961 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,397 software developers and data experts.

pointer one past malloc.ed memory

Hi Gurus

I've tried to come up with a small logical example of my problem.
The problem is platform specific (MIPS) which I understand should not
be discussed here.

So here goes my example:

Code is doing malloc of variable sizes.
The last byte of malloc.ed memory is written a magic.
Since the size if variable (and iit ncludes magic), the code adds size
and subtracts sizeof magic to access the magic.
Now this is where the problem comes.
The calculation of address generates an overflow exception.

Let me give an example (it's a corner case I ran into)
0x8000 0000 is end of the malloc.ed memory and magic is 8 bytes
so it is a lw $v0, -8($v0)

0x8000 0000 + -8 is generating an overflow exception for address.
The address 0x7fff fff8, is perfectly valid to dereference but
*computation* of the address is overflowing.

Sorry i had to give some platform specific details.
But I am hoping it will clarify my question.

Now my question to you about C language is:
Can we have the address 1 byte beyond the malloc.ed area (no it is not
dereferenced) and then a negative offset into it to legitimately
dereference the memory malloc.ed by malloc()?
What does the standard say here?
Or is it just another case of overflow and we can not blame MIPS
load-store unit for overflown address generation (thats what I think).

I've solved the problem by first subtracting 8 then adding size . i.e.
instead of load from -8(ptr) I forced the code to do ptr to point to 8
bytes earlier and then load from 0(ptr). But I want to know what the
standard says about the address of malloc.ed memory.

Thanks
- Ramachandran
Nov 14 '05 #1
11 1913
Sushil wrote:
Can we have the address 1 byte beyond the malloc.ed area (no it is not
dereferenced) and then a negative offset into it to legitimately
dereference the memory malloc.ed by malloc()?


Yes.

--
pete
Nov 14 '05 #2
Sushil wrote:
Hi Gurus

I've tried to come up with a small logical example of my problem.
The problem is platform specific (MIPS) which I understand should not
be discussed here.

So here goes my example:

Code is doing malloc of variable sizes.
The last byte of malloc.ed memory is written a magic.
Since the size if variable (and iit ncludes magic), the code adds size
and subtracts sizeof magic to access the magic.
Now this is where the problem comes.
The calculation of address generates an overflow exception.

Let me give an example (it's a corner case I ran into)
0x8000 0000 is end of the malloc.ed memory and magic is 8 bytes
so it is a lw $v0, -8($v0)

0x8000 0000 + -8 is generating an overflow exception for address.
The address 0x7fff fff8, is perfectly valid to dereference but
*computation* of the address is overflowing.

Sorry i had to give some platform specific details.
But I am hoping it will clarify my question.

Now my question to you about C language is:
Can we have the address 1 byte beyond the malloc.ed area (no it is not
dereferenced) and then a negative offset into it to legitimately
dereference the memory malloc.ed by malloc()?
What does the standard say here?
Or is it just another case of overflow and we can not blame MIPS
load-store unit for overflown address generation (thats what I think).

I've solved the problem by first subtracting 8 then adding size . i.e.
instead of load from -8(ptr) I forced the code to do ptr to point to 8
bytes earlier and then load from 0(ptr). But I want to know what the
standard says about the address of malloc.ed memory.

Thanks
- Ramachandran


We generally don't know MIPS assembler and as a C programmer, you
shouldn't care. If you care to post some C code which exhibits the
problem, several people here might be more able to help you.

Note that 0x80000000 is -2147483648 or INT_MIN on 32-bit systems.

--
Joe Wright mailto:jo********@comcast.net
"Everything should be made as simple as possible, but not simpler."
--- Albert Einstein ---
Nov 14 '05 #3
> We generally don't know MIPS assembler and as a C programmer, you
shouldn't care. If you care to post some C code which exhibits the
problem, several people here might be more able to help you.

Note that 0x80000000 is -2147483648 or INT_MIN on 32-bit systems.


Thanks for your reply Joe and Pete.

The simplified code is like this
magic_struct *magic_ptr = ((magic_struct *) ((uchar *) ptr +
ptr->size))- 1;
where magic_struct contains two magic ulongs.

The issue is ptr + ptr->size becomes 0x80000000 and -1 after pointer
arithmetic is 8 bytes since sizeof(magic_struct) is 8. However, the
compiler does not do the -8 part since it is compiled with -O3 (okay I
know this is offtopic). Instead, when magic_ptr->magic1 is referenced
(later) compiler offsets -8 from the above address at that point. This
is overflowing and causing an address exception.

The way I fixed it is I rearranged code
magic_struct *magicptr = ((uchar *) ptr - sizeof(magic_struct)) +
ptr->size;

Question 1:
Do you think this trick(the parentheses) will force it to calculate
entire thing (it is working right now - but I am not confident it
always will). I want to make sure that the compiler calculates
magic_ptr fully and offsets 0 from it instead of optimising and
offseting -8. The code is in fast path and I dont wish to turn off
optimisation. I guess the standard does not specify any of this.
But if you've some wisdom from experience, please enlighten me.

Question 2:
What does the language say about the address calculation.
What is the signedness of address when it comes to address arithemtic.
For example pointer to a struct. Is it signed or unsigned.

I'm not a clc language lawyer and I am learning C :-) so please bear
with me.
Thanks for your understanding and tolerance and sharing your C
knowledge.

You guys are very helpful,
With regards
- Sushil Ramachandran
Nov 14 '05 #4
Sushil wrote:
We generally don't know MIPS assembler and as a C programmer, you
shouldn't care. If you care to post some C code which exhibits the
problem, several people here might be more able to help you.

Note that 0x80000000 is -2147483648 or INT_MIN on 32-bit systems.
Thanks for your reply Joe and Pete.

The simplified code is like this
magic_struct *magic_ptr = ((magic_struct *) ((uchar *) ptr +
ptr->size))- 1;
where magic_struct contains two magic ulongs.

The issue is ptr + ptr->size becomes 0x80000000 and -1 after pointer
arithmetic is 8 bytes since sizeof(magic_struct) is 8. However, the
compiler does not do the -8 part since it is compiled with -O3 (okay I
know this is offtopic). Instead, when magic_ptr->magic1 is referenced
(later) compiler offsets -8 from the above address at that point. This
is overflowing and causing an address exception.

The way I fixed it is I rearranged code
magic_struct *magicptr = ((uchar *) ptr - sizeof(magic_struct)) +
ptr->size;


If ptr is the lowest byte of the object,
then it is not safe to subtract from it.
It is safe to calculate the integer part first
and then add it the pointer.

magic_struct *magicptr
= ptr->size - sizeof(magic_struct) + (uchar *)ptr;

Question 1:
Do you think this trick(the parentheses) will force it to calculate
entire thing (it is working right now - but I am not confident it
always will).
No.
Question 2:
What does the language say about the address calculation.
There is a rule which explicitly allows the calculation
of an address one byte beyond an object, as long as it
is not dereferenced. There is no such rule for addresses
lower than an object lowest addressable byte.
What is the signedness of address when it comes to address arithemtic.
For example pointer to a struct. Is it signed or unsigned.


No. Pointers are scalar but not integer types.
The difference between two pointers,
is a signed integer type called ptrdiff_t, defined in stddef.h

--
pete
Nov 14 '05 #5
in*******@yahoo.com (Sushil) wrote:
We generally don't know MIPS assembler and as a C programmer, you
shouldn't care. If you care to post some C code which exhibits the
problem, several people here might be more able to help you.

Note that 0x80000000 is -2147483648 or INT_MIN on 32-bit systems.
Thanks for your reply Joe and Pete.

The simplified code is like this
magic_struct *magic_ptr = ((magic_struct *) ((uchar *) ptr +
ptr->size))- 1;
where magic_struct contains two magic ulongs.


I presume uchar is just a typedef for unsigned char? If not, that's your
bug right there.
The issue is ptr + ptr->size becomes 0x80000000 and -1 after pointer
arithmetic is 8 bytes since sizeof(magic_struct) is 8. However, the
compiler does not do the -8 part since it is compiled with -O3 (okay I
know this is offtopic). Instead, when magic_ptr->magic1 is referenced
(later) compiler offsets -8 from the above address at that point. This
is overflowing and causing an address exception.
Hohum... I don't think that's conforming. If there really is a block of
memory of ptr->size bytes at ptr, then (char *)ptr + ptr->size _must_ be
a valid pointer, albeit a non-dereferencable one. Since what you're
doing with it does _not_ involve dereferencing, you should be all right.

Perhaps you should post the definition of a magic_struct, and the
declaration and initialisation of ptr. It may be that not all is what it
seems.
The way I fixed it is I rearranged code
magic_struct *magicptr = ((uchar *) ptr - sizeof(magic_struct)) +
ptr->size;
This, however, is not correct. You create an intermediate pointer,
(uchar *)ptr - sizeof (magic_struct), which is before the start of the
block - at least, if ptr points at the start of it, which seems to be
the case.

This is how I would do it:

magic_struct *magicptr=(uchar *)ptr + (ptr->size - sizeof *magicptr);

This calculates the size of the offset first, and then adds it to the
pointer, guaranteeing that, as long as the result lies within the memory
belonging to ptr and ptr->size fits in a size_t, no intermediate result
can overflow.
Question 1:
Do you think this trick(the parentheses) will force it to calculate
entire thing (it is working right now - but I am not confident it
always will). I want to make sure that the compiler calculates
magic_ptr fully and offsets 0 from it instead of optimising and
offseting -8.
_Nothing_ in ISO C can stop your compiler from optimising, just as
nothing can force it to optimise. Efficiency falls beyond the scope of
the Standard. Basically, as long as your implementation does _what_ it
is required to do, ISO C doesn't care _how_ it does it, and even less
how efficiently.
Question 2:
What does the language say about the address calculation.
What is the signedness of address when it comes to address arithemtic.
For example pointer to a struct. Is it signed or unsigned.


That cannot be answered. You can only:
- add or subtract an integer to (from) an object pointer;
- subtract two pointers to the same object.
Both of these are well-defined; neither of them involves a "sign" of the
pointer. Moreover, for both of these, it does not matter what kind of
object the pointer(s) point(s) at; adding an integer to a struct pointer
works exactly the same way as adding an integer to a char pointer,
except for the size of the objects.

Richard
Nov 14 '05 #6
Joe Wright wrote:

Note that 0x80000000 is -2147483648 or INT_MIN on 32-bit systems.


0x80000000 is the *positive* value 2147483648.
If this value exceeds INT_MAX (as it will with 32-bit
integers), the constant does not have `int' type but
either `unsigned int' (if UINT_MAX is big enough)
or `long' (if LONG_MAX is big enough) or `unsigned long'
(ULONG_MAX is guaranteed to be big enough).

--
Er*********@sun.com

Nov 14 '05 #7
Summary, including machine-specific info that may or may not be
relevant:

a) The hardware traps integer overflow, including address arithmetic
overflow. (C unsigned types use non-trapping instructions, and
in fact, C signed types generally use these as well, but the
pointer-offset address format apparently uses the trapping form
of arithmetic.)

b) The C code in question is somehow obtaining a pointer value that
lies near the end of 32-bit-integer space, 0x7ffffff8 for
instance.

c) The compiler generates non-trapping code when not optimizing, but
trapping code when optimising.

[Someone wrote:]
We generally don't know MIPS assembler and as a C programmer, you
shouldn't care. If you care to post some C code which exhibits the
problem, several people here might be more able to help you.

Note that 0x80000000 is -2147483648 or INT_MIN on 32-bit systems.

In article <25**************************@posting.google.com >
Sushil <in*******@yahoo.com> writes:Thanks for your reply Joe and Pete.

The simplified code is like this
magic_struct *magic_ptr = ((magic_struct *) ((uchar *) ptr + ptr->size))- 1;
where magic_struct contains two magic ulongs.

The issue is ptr + ptr->size becomes 0x80000000 and -1 after pointer
arithmetic is 8 bytes since sizeof(magic_struct) is 8. However, the
compiler does not do the -8 part since it is compiled with -O3 (okay I
know this is offtopic).
Actually, it may not be off-topic (although it is not "on" topic
either -- it might just be irrelevant, as far as conformance goes).
Instead, when magic_ptr->magic1 is referenced (later) compiler offsets
-8 from the above address at that point. This is overflowing and
causing an address exception.
Specifically, the compiler transforms:

struct S *p;
...
p = (expr) - 1; /* where (expr) has type "struct S *" and
points "just past the end" of an array of "struct S" */
use(p->foo);

into:

p = expr;
use(p[-1].foo);

when asked to optimize (via -O3). When not optimizing, the compiler
actually computes "p = expr - 1" as originally directed, then uses
p[0].foo to access p->foo, generating address offsets of the form
0(reg) instead of -8(reg). When optimizing, the compiler uses the
second form. As it happens, "reg" holds 0x7ffffff8 when offset by
0 (not optimizing) or 0x80000000 when offset by -8 (optimizing).
The former works, and the latter does not.

The expression I have denoted "expr" here has the general form of
"one past the last byte in an object". The C standard explicitly
allows such computations, and you may even use the second form
yourself in your C code. The system is obliged, as if by contract,
to make this work (in Standard C).

If the value in "expr" is obtained by ordinary (and conforming) C
code (see example below), the compiler optimization occurring here
causes the overall system to fail to conform. I suggest that the
bug here is either that the system is handing out "the last address"
-- it should make sure that the expression "expr" never goes "above"
0x7fffffff, i.e., it should never put an object beyond address
0x7ffffffe, and even further back from the edge for objects larger
than 1 byte -- or else the compiler must not perform the optimization
it is doing.

On the other hand, if the value in "ptr" is obtained by something
not part of Standard C, the system as a whole can still conform.
That is, the Standard C part of the system might in fact hand out
address 0x7ffffffe as a valid one-byte object, but be careful to
make sure that all valid forms of address arithmetic -- including
the "one byte past the end" cases -- never exceed 0x7fffffff. In
this case, no matter how big p is, p+1 will be 0x7fffffff at most,
and negative offsets from a register holding p will work.

Assuming the Standard C part of the system conforms (by not handing
out "address smack dab at end of address space"), whether handing
out such an address is a bug in the non-"Standard C" part of the
system is up to the non-"Standard C" part. That is, the Standard
cannot tell you whether this is right or wrong -- only the system's
own documentation about its own special non-standard extensions
and behaviors can say so.
The way I fixed it is I rearranged code
magic_struct *magicptr = ((uchar *) ptr - sizeof(magic_struct)) +
ptr->size;

Question 1:
Do you think this trick(the parentheses) will force it to [work] ...
Not in general, no. (Tricks involving variables marked "volatile"
will, though.)
Question 2:
What does the language say about the address calculation.


Virtually nothing. The language *does* say that it is legal to
compute an address "one beyond the end" of any object, so if:

char *p;
size_t size;
struct S *q;
...
size = N * sizeof *q; /* where N > 0 */
p = malloc(size);
if (p == NULL) ... handle error ...
p += size;
q = p;
q[-1].field = val;

fails at runtime, the compiler fails to conform. *How* the C system
makes this work -- whether that involves any signedness of addresses,
for instance -- is not specified. On the other hand, as soon as you
write something like:

p = (char *)0x7ffffff8;

you have left the Standard behind in the dust, and can no longer
ask anything from it. You are now at the mercy of your particular
system. If:

p += 8;
q = p;
q[-1].field = val;

subsequently fails, the Standard cannot help. This is true EVEN
IF THIS OCCURS ELSEWHERE IN THE CODE, using a different value in
p! A single occurrence of "undefined behavior" anywhere officially
terminates your "contract" with the C Standard. In practice, of
course, if the original undefined behavior is harmless on your
system, it probably has no effect on "disconnected" code -- which
is handy when debugging -- but strange things can happen. (For
instance, suppose the first bit of "undefined behavior" you invoke
is something like:

_implementation_hook("set time bomb to go off in 5 minutes");

If and when the time bomb does go off, it can affect everything.)
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
Nov 14 '05 #8
Chris Torek <no****@torek.net> wrote in message news:<cd*********@news2.newsguy.com>...
Summary, including machine-specific info that may or may not be
relevant:


I want to thank you Chris (and others).
That was a very detailed explanation.

clc rocks.
-Sushil Ramachandran
Nov 14 '05 #9
pete <pf*****@mindspring.com> wrote in message news:<40***********@mindspring.com>...
The way I fixed it is I rearranged code
magic_struct *magicptr = ((uchar *) ptr - sizeof(magic_struct)) +
ptr->size;


If ptr is the lowest byte of the object,
then it is not safe to subtract from it.
It is safe to calculate the integer part first
and then add it the pointer.

magic_struct *magicptr
= ptr->size - sizeof(magic_struct) + (uchar *)ptr;

Question 1:
Do you think this trick(the parentheses) will force it to calculate
entire thing (it is working right now - but I am not confident it
always will).


No.


Turns out that in some other release of the above "fix", it indeed did
not work. The trick failed. So now I want to know why you said that it
wont work.

originally
a = x + y - z;

my "fix" to avoid overflow
a = x + (y-z);

1. Either there is flaw in my "fix" and in my assumption that x + (y -
z) will avoid possible overflow in x + y - z or the compiler has a
bug.

Does not (y-z) cause a precedence?
If the compiler optimises x + (y - z) and yet generates code
equivalent to x + y - z then can I say that the compiler is broken? Or
is it not fair to ask compiler folks to make their optimisation ouput
ISO C conforming?

If you think it is broken, can someone point me to the relevant text
in standard that will explain this (so that I can point the compiler
folks to it)?

2. I even did this
temp = y -z;
a = x + temp;
It did not work (but I am not surprised at that) since it's a simple
optimisation for the compiler.

So in essense
expression1;
expression2;

Is not the compiler supposed to finish expression1 and then generate
code for expression2? Is not that a 'sequence point'?

I'm asked to turn off the optimisation (which I can not do :-( but it
does generate the correct code) but then, in real life, there has to
be a balance between no optimisation and optimisation with *correct*
code.

<OT>
I suspect that it is because the function which has this code is a
static inline and so there is code above and below it, and so by the
time the optimiser kicks in, it may "lose" the parenthesis. Turning
off gcse flag (global common subexpression elimination pass) worked.
Removing the inline worked too - perhaps because the compiler had
lesser chance to optimise and move things around since it's a small
function.
</OT>

3.Finally how can I enforce (y-z) evaluation in
a = x + (y - z);

Seeking enlightment :-)
-Sushil
Nov 14 '05 #10
Sushil wrote:

pete <pf*****@mindspring.com> wrote in message news:<40***********@mindspring.com>...
The way I fixed it is I rearranged code
magic_struct *magicptr = ((uchar *) ptr - sizeof(magic_struct)) +
ptr->size;


If ptr is the lowest byte of the object,
then it is not safe to subtract from it.
It is safe to calculate the integer part first
and then add it the pointer.

magic_struct *magicptr
= ptr->size - sizeof(magic_struct) + (uchar *)ptr;

Question 1:
Do you think this trick(the parentheses)
will force it to calculate
entire thing (it is working right now - but I am not confident it
always will).


No.


Turns out that in some other release of the above "fix", it indeed did
not work. The trick failed. So now I want to know why you said that it
wont work.

originally
a = x + y - z;

my "fix" to avoid overflow
a = x + (y-z);

1. Either there is flaw in my "fix" and in my assumption that x + (y -
z) will avoid possible overflow in x + y - z or the compiler has a
bug.

Does not (y-z) cause a precedence?


Addition and subtraction between pointers and integers,
is more complicated than addition and subtraction
between just integers.

(y-z) is an undefined expression, if y is the address of an object,
which is not part of an aggregate object, and z is positive.

(x+y) has potential overflow, as you stated.

(x-z) is a positive number which won't cause overflow.
It had better be, or your assignment has no chance.

You want y+(x-z), more simpley expressed as: x-z+y

(big_size - small_size + pointer)
Nov 14 '05 #11
In article <news:25*************************@posting.google.c om>
(and some other article, perhaps the one at the top of the references
tree) Sushil <in*******@yahoo.com> wrote:
Do you think this trick(the parentheses) will force it to calculate
entire thing (it is working right now - but I am not confident it
always will).
Turns out that in some other release of the above "fix", it indeed did
not work. ... 3.Finally how can I enforce (y-z) evaluation in
a = x + (y - z);


This is not the real problem (at least, not as you described it
originally). I already hinted how to do it. Suppose your structure
type is S:

struct S *p = expr1 - expr2;
... p->field ...

The compiler is optimizing this into:

struct S *p = expr1;
... p[-expr2].field ...

Normally this would be OK, but you are going outside the C standard
(I suspect), so that "expr1" is not a valid C pointer nor the address
"one byte beyond the end" of a valid C object.

The solution at which I hinted is to pass the result of the
subtraction through a volatile-qualified variable:

struct S *p;
volatile struct S *tmp;

tmp = expr1 - expr2;
p = (struct S *)tmp;

The removal of the "volatile" qualifier requires a cast (unfortunately)
but the act of passing the value through "tmp" will force the subtraction
to occur earlier, despite any other optimization.

This does depend on a lot of slightly-shaky assumptions about your
compiler(s), but I predict it will work on your system.
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
Nov 14 '05 #12

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

Similar topics

2
by: Yossarian | last post by:
Hi, I'm a bit confused about something, hopefully someone can put me straight. I'd like to be able to call a function which takes a pointer to pointer, have that function allocate memory and...
7
by: Fatted | last post by:
I'm trying to learn how to create arrays dynamically. But its just not happening. Have a look at code below and point and laugh where appropriate... First part of program, I'm using an array of...
42
by: junky_fellow | last post by:
Consider an implementation that doesn't use all bits 0 to represent a NULL pointer. Let the NULL pointer is represented by 0x12345678. On such an implementation, if the value of NULL pointer is...
8
by: ulyses | last post by:
I'm trying to put pointer to flexible array of structures in other structure. I want to have pointer to array of pixels in screen structure. Here is mine code, but I think it isn't quite all right:...
27
by: Erik de Castro Lopo | last post by:
Hi all, The GNU C compiler allows a void pointer to be incremented and the behaviour is equivalent to incrementing a char pointer. Is this legal C99 or is this a GNU C extention? Thanks in...
26
by: Bill Reid | last post by:
Bear with me, as I am not a "professional" programmer, but I was working on part of program that reads parts of four text files into a buffer which I re-allocate the size as I read each file. I...
2
by: Mike | last post by:
Hi, I am new to C and having problems with the following program. Basically I am trying to read some files, loading data structures into memory for latter searching. I am trying to use structres...
12
by: John Goche | last post by:
A lot of C++ code allocates a buffer and initializes start and end pointers as follows: +-------------------------------+ + + +-------------------------------+ ^ ...
17
by: Ivan K. | last post by:
I am looking at some legacy code, which begins by allocating a double matrix with the dmatrix() function from NRC as follows: double **A, **augin, **augout, **aa; A = dmatrix(1,...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
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
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...

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.